<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/
// Written by Wang Rui, (C) 2010

#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/Registry>
#include <osgDB/ObjectWrapper>
#include "AsciiStreamOperator.h"
#include "BinaryStreamOperator.h"
#include "XmlStreamOperator.h"

using namespace osgDB;

#define CATCH_EXCEPTION(s) \
    if (s.getException()) return (s.getException()->getError() + " At " + s.getException()->getField());

InputIterator* readInputIterator( std::istream& fin, const Options* options )
{
    bool extensionIsAscii = false, extensionIsXML = false;
    if ( options )
    {
        const std::string& optionString = options->getOptionString();
        if ( optionString.find("Ascii")!=std::string::npos ) extensionIsAscii = true;
        else if ( optionString.find("XML")!=std::string::npos ) extensionIsXML = true;
    }

    if ( !extensionIsAscii && !extensionIsXML )
    {
        unsigned int headerLow = 0, headerHigh = 0;
        fin.read( (char*)&headerLow, INT_SIZE );
        fin.read( (char*)&headerHigh, INT_SIZE );
        if ( headerLow==OSG_HEADER_LOW && headerHigh==OSG_HEADER_HIGH )
        {
            return new BinaryInputIterator(&fin);
        }
        fin.seekg( 0, std::ios::beg );
    }

    if ( !extensionIsXML )
    {
        std::string header; fin >> header;
        if ( header=="#Ascii" )
        {
            return new AsciiInputIterator(&fin);
        }
        fin.seekg( 0, std::ios::beg );
    }

    if ( 1 )
    {
        std::string header; std::getline( fin, header );
        if ( !header.compare(0, 5, "<?xml") )
        {
            return new XmlInputIterator(&fin);
        }
        fin.seekg( 0, std::ios::beg );
    }
    return NULL;
}

OutputIterator* writeOutputIterator( std::ostream& fout, const Options* options )
{
    // Read precision parameter, for text & XML formats
    int precision(-1);
    if ( options ) {
        std::istringstream iss(options->getOptionString());
        std::string opt;
        while (iss >> opt)
        {
            if(opt=="PRECISION" || opt=="precision")
            {
                iss >> precision;
            }
        }
    }

    if ( options && options->getOptionString().find("Ascii")!=std::string::npos )
    {
        fout << std::string("#Ascii") << ' ';
        return new AsciiOutputIterator(&fout, precision);
    }
    else if ( options && options->getOptionString().find("XML")!=std::string::npos )
    {
        fout << std::string("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>") << std::endl;
        return new XmlOutputIterator(&fout, precision);
    }
    else
    {
        unsigned int low = OSG_HEADER_LOW, high = OSG_HEADER_HIGH;
        fout.write( (char*)&low, INT_SIZE );
        fout.write( (char*)&high, INT_SIZE );
        return new BinaryOutputIterator(&fout);
    }
}

class ReaderWriterOSG2 : public osgDB::ReaderWriter
{
public:
    ReaderWriterOSG2()
    {
        supportsExtension( "osg2", "OpenSceneGraph extendable format" );
        supportsExtension( "osgt", "OpenSceneGraph extendable ascii format" );
        supportsExtension( "osgb", "OpenSceneGraph extendable binary format" );
        supportsExtension( "osgx", "OpenSceneGraph extendable XML format" );

        supportsOption( "Ascii", "Import/Export option: Force reading/writing ascii file" );
        supportsOption( "XML", "Import/Export option: Force reading/writing XML file" );
        supportsOption( "ForceReadingImage", "Import option: Load an empty image instead if required file missed" );
        supportsOption( "SchemaData", "Export option: Record inbuilt schema data into a binary file" );
        supportsOption( "SchemaFile=<file>", "Import/Export option: Use/Record an ascii schema file" );
        supportsOption( "Compressor=<name>", "Export option: Use an inbuilt or user-defined compressor" );
        supportsOption( "WriteImageHint=<hint>", "Export option: Hint of writing image to stream: "
                        "<IncludeData> writes Image::data() directly; "
                        "<IncludeFile> writes the image file itself to stream; "
                        "<UseExternal> writes only the filename; "
                        "<WriteOut> writes Image::data() to disk as external file." );
    }

    virtual const char* className() const { return "OpenSceneGraph Native Format Reader/Writer"; }

    Options* prepareReading( ReadResult& result, std::string& fileName, std::ios::openmode& mode, const Options* options ) const
    {
        std::string ext = osgDB::getLowerCaseFileExtension( fileName );
        if ( !acceptsExtension(ext) )
        {
            result = ReadResult::FILE_NOT_HANDLED;
            return 0;
        }
        fileName = osgDB::findDataFile( fileName, options );
        if ( fileName.empty() )
        {
            result = ReadResult::FILE_NOT_FOUND;
            return 0;
        }

        osg::ref_ptr<Options> local_opt = options ?
            static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
        local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));
        if ( ext=="osgt" ) local_opt->setOptionString( local_opt->getOptionString() + " Ascii" );
        else if ( ext=="osgx" ) local_opt->setOptionString( local_opt->getOptionString() + " XML" );
        else mode |= std::ios::binary;

        return local_opt.release();
    }

    virtual ReadResult readObject( const std::string& file, const Options* options ) const
    {
        ReadResult result = ReadResult::FILE_LOADED;
        std::string fileName = file;
        std::ios::openmode mode = std::ios::in;
        Options* local_opt = prepareReading( result, fileName, mode, options );
        if ( !result.success() ) return result;

        osgDB::ifstream istream( fileName.c_str(), mode );
        return readObject( istream, local_opt );
    }

    virtual ReadResult readObject( std::istream& fin, const Options* options ) const
    {
        osg::ref_ptr<InputIterator> ii = readInputIterator(fin, options);
        if ( !ii ) return ReadResult::FILE_NOT_HANDLED;

        InputStream is( options );

        osgDB::InputStream::ReadType readType = is.start(ii.get());
        if ( readType==InputStream::READ_UNKNOWN )
        {
            CATCH_EXCEPTION(is);
            return ReadResult::FILE_NOT_HANDLED;
        }
        is.decompress(); CATCH_EXCEPTION(is);

        osg::Object* obj = is.readObject(); CATCH_EXCEPTION(is);
        return obj;
    }

    virtual ReadResult readImage( const std::string& file, const Options* options ) const
    {
        ReadResult result = ReadResult::FILE_LOADED;
        std::string fileName = file;
        std::ios::openmode mode = std::ios::in;
        Options* local_opt = prepareReading( result, fileName, mode, options );
        if ( !result.success() ) return result;

        osgDB::ifstream istream( fileName.c_str(), mode );
        return readImage( istream, local_opt );
    }

    virtual ReadResult readImage( std::istream& fin, const Options* options ) const
    {
        osg::ref_ptr<InputIterator> ii = readInputIterator(fin, options);
        if ( !ii ) return ReadResult::FILE_NOT_HANDLED;

        InputStream is( options );
        if ( is.start(ii.get())!=InputStream::READ_IMAGE )
        {
            CATCH_EXCEPTION(is);
            return ReadResult::FILE_NOT_HANDLED;
        }
        is.decompress(); CATCH_EXCEPTION(is);
        osg::Image* image = is.readImage(); CATCH_EXCEPTION(is);
        return image;
    }

    virtual ReadResult readNode( const std::string& file, const Options* options ) const
    {
        ReadResult result = ReadResult::FILE_LOADED;
        std::string fileName = file;
        std::ios::openmode mode = std::ios::in;
        Options* local_opt = prepareReading( result, fileName, mode, options );
        if ( !result.success() ) return result;

        osgDB::ifstream istream( fileName.c_str(), mode );
        return readNode( istream, local_opt );
    }

    virtual ReadResult readNode( std::istream& fin, const Options* options ) const
    {
        osg::ref_ptr<InputIterator> ii = readInputIterator(fin, options);
        if ( !ii ) return ReadResult::FILE_NOT_HANDLED;

        InputStream is( options );
        if ( is.start(ii.get())!=InputStream::READ_SCENE )
        {
            CATCH_EXCEPTION(is);
            return ReadResult::FILE_NOT_HANDLED;
        }

        is.decompress(); CATCH_EXCEPTION(is);
        osg::Node* node = dynamic_cast<osg::Node*>(is.readObject()); CATCH_EXCEPTION(is);
        return node;
    }

    Options* prepareWriting( WriteResult& result, const std::string& fileName, std::ios::openmode& mode, const Options* options ) const
    {
        std::string ext = osgDB::getFileExtension( fileName );
        if ( !acceptsExtension(ext) ) result = WriteResult::FILE_NOT_HANDLED;

        osg::ref_ptr<Options> local_opt = options ?
            static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
        local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));
        if ( ext=="osgt" ) local_opt->setOptionString( local_opt->getOptionString() + " Ascii" );
        else if ( ext=="osgx" ) local_opt->setOptionString( local_opt->getOptionString() + " XML" );
        else mode |= std::ios::binary;

        return local_opt.release();
    }

    virtual WriteResult writeObject( const osg::Object& object, const std::string& fileName, const Options* options ) const
    {
        WriteResult result = WriteResult::FILE_SAVED;
        std::ios::openmode mode = std::ios::out;
        osg::ref_ptr<Options> local_opt = prepareWriting( result, fileName, mode, options );
        if ( !result.success() ) return result;

        osgDB::ofstream fout( fileName.c_str(), mode );
        if ( !fout ) return WriteResult::ERROR_IN_WRITING_FILE;

        result = writeObject( object, fout, local_opt.get() );
        fout.close();
        return result;
    }

    virtual WriteResult writeObject( const osg::Object& object, std::ostream& fout, const Options* options ) const
    {
        osg::ref_ptr<OutputIterator> oi = writeOutputIterator(fout, options);

        OutputStream os( options );
        os.start( oi.get(), OutputStream::WRITE_OBJECT ); CATCH_EXCEPTION(os);
        os.writeObject( &object ); CATCH_EXCEPTION(os);
        os.compress( &fout ); CATCH_EXCEPTION(os);

        oi->flush();
        if ( !os.getSchemaName().empty() )
        {
            osgDB::ofstream schemaStream( os.getSchemaName().c_str(), std::ios::out );
            if ( !schemaStream.fail() ) os.writeSchema( schemaStream );
            schemaStream.close();
        }

        if ( fout.fail() ) return WriteResult::ERROR_IN_WRITING_FILE;
        return WriteResult::FILE_SAVED;
    }

    virtual WriteResult writeImage( const osg::Image& image, const std::string& fileName, const Options* options ) const
    {
        WriteResult result = WriteResult::FILE_SAVED;
        std::ios::openmode mode = std::ios::out;
        osg::ref_ptr<Options> local_opt = prepareWriting( result, fileName, mode, options );
        if ( !result.success() ) return result;

        osgDB::ofstream fout( fileName.c_str(), mode );
        if ( !fout ) return WriteResult::ERROR_IN_WRITING_FILE;

        result = writeImage( image, fout, local_opt.get() );
        fout.close();
        return result;
    }

    virtual WriteResult writeImage( const osg::Image& image, std::ostream& fout, const Options* options ) const
    {
        osg::ref_ptr<OutputIterator> oi = writeOutputIterator(fout, options);

        OutputStream os( options );
        os.start( oi.get(), OutputStream::WRITE_IMAGE ); CATCH_EXCEPTION(os);
        os.writeImage( &image ); CATCH_EXCEPTION(os);
        os.compress( &fout ); CATCH_EXCEPTION(os);

        oi->flush();
        if ( !os.getSchemaName().empty() )
        {
            osgDB::ofstream schemaStream( os.getSchemaName().c_str(), std::ios::out );
            if ( !schemaStream.fail() ) os.writeSchema( schemaStream );
            schemaStream.close();
        }

        if ( fout.fail() ) return WriteResult::ERROR_IN_WRITING_FILE;
        return WriteResult::FILE_SAVED;
    }

    virtual WriteResult writeNode( const osg::Node& node, const std::string& fileName, const Options* options ) const
    {
        WriteResult result = WriteResult::FILE_SAVED;
        std::ios::openmode mode = std::ios::out;
        osg::ref_ptr<Options> local_opt = prepareWriting( result, fileName, mode, options );
        if ( !result.success() ) return result;

        osgDB::ofstream fout( fileName.c_str(), mode );
        if ( !fout ) return WriteResult::ERROR_IN_WRITING_FILE;

        result = writeNode( node, fout, local_opt.get() );
        fout.close();
        return result;
    }

    virtual WriteResult writeNode( const osg::Node& node, std::ostream& fout, const Options* options ) const
    {
        osg::ref_ptr<OutputIterator> oi = writeOutputIterator(fout, options);

        OutputStream os( options );
        os.start( oi.get(), OutputStream::WRITE_SCENE ); CATCH_EXCEPTION(os);
        os.writeObject( &node ); CATCH_EXCEPTION(os);
        os.compress( &fout ); CATCH_EXCEPTION(os);

        oi->flush();
        if ( !os.getSchemaName().empty() )
        {
            osgDB::ofstream schemaStream( os.getSchemaName().c_str(), std::ios::out );
            if ( !schemaStream.fail() ) os.writeSchema( schemaStream );
            schemaStream.close();
        }

        if ( fout.fail() ) return WriteResult::ERROR_IN_WRITING_FILE;
        return WriteResult::FILE_SAVED;
    }
};

REGISTER_OSGPLUGIN( osg2, ReaderWriterOSG2 )
<commit_msg>From Colin McDonald, fix for Solaris build.<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/
// Written by Wang Rui, (C) 2010

#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/Registry>
#include <osgDB/ObjectWrapper>
#include <stdlib.h>
#include "AsciiStreamOperator.h"
#include "BinaryStreamOperator.h"
#include "XmlStreamOperator.h"

using namespace osgDB;

#define CATCH_EXCEPTION(s) \
    if (s.getException()) return (s.getException()->getError() + " At " + s.getException()->getField());

InputIterator* readInputIterator( std::istream& fin, const Options* options )
{
    bool extensionIsAscii = false, extensionIsXML = false;
    if ( options )
    {
        const std::string& optionString = options->getOptionString();
        if ( optionString.find("Ascii")!=std::string::npos ) extensionIsAscii = true;
        else if ( optionString.find("XML")!=std::string::npos ) extensionIsXML = true;
    }

    if ( !extensionIsAscii && !extensionIsXML )
    {
        unsigned int headerLow = 0, headerHigh = 0;
        fin.read( (char*)&headerLow, INT_SIZE );
        fin.read( (char*)&headerHigh, INT_SIZE );
        if ( headerLow==OSG_HEADER_LOW && headerHigh==OSG_HEADER_HIGH )
        {
            return new BinaryInputIterator(&fin);
        }
        fin.seekg( 0, std::ios::beg );
    }

    if ( !extensionIsXML )
    {
        std::string header; fin >> header;
        if ( header=="#Ascii" )
        {
            return new AsciiInputIterator(&fin);
        }
        fin.seekg( 0, std::ios::beg );
    }

    if ( 1 )
    {
        std::string header; std::getline( fin, header );
        if ( !header.compare(0, 5, "<?xml") )
        {
            return new XmlInputIterator(&fin);
        }
        fin.seekg( 0, std::ios::beg );
    }
    return NULL;
}

OutputIterator* writeOutputIterator( std::ostream& fout, const Options* options )
{
    // Read precision parameter, for text & XML formats
    int precision(-1);
    if ( options ) {
        std::istringstream iss(options->getOptionString());
        std::string opt;
        while (iss >> opt)
        {
            if(opt=="PRECISION" || opt=="precision")
            {
                iss >> precision;
            }
        }
    }

    if ( options && options->getOptionString().find("Ascii")!=std::string::npos )
    {
        fout << std::string("#Ascii") << ' ';
        return new AsciiOutputIterator(&fout, precision);
    }
    else if ( options && options->getOptionString().find("XML")!=std::string::npos )
    {
        fout << std::string("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>") << std::endl;
        return new XmlOutputIterator(&fout, precision);
    }
    else
    {
        unsigned int low = OSG_HEADER_LOW, high = OSG_HEADER_HIGH;
        fout.write( (char*)&low, INT_SIZE );
        fout.write( (char*)&high, INT_SIZE );
        return new BinaryOutputIterator(&fout);
    }
}

class ReaderWriterOSG2 : public osgDB::ReaderWriter
{
public:
    ReaderWriterOSG2()
    {
        supportsExtension( "osg2", "OpenSceneGraph extendable format" );
        supportsExtension( "osgt", "OpenSceneGraph extendable ascii format" );
        supportsExtension( "osgb", "OpenSceneGraph extendable binary format" );
        supportsExtension( "osgx", "OpenSceneGraph extendable XML format" );

        supportsOption( "Ascii", "Import/Export option: Force reading/writing ascii file" );
        supportsOption( "XML", "Import/Export option: Force reading/writing XML file" );
        supportsOption( "ForceReadingImage", "Import option: Load an empty image instead if required file missed" );
        supportsOption( "SchemaData", "Export option: Record inbuilt schema data into a binary file" );
        supportsOption( "SchemaFile=<file>", "Import/Export option: Use/Record an ascii schema file" );
        supportsOption( "Compressor=<name>", "Export option: Use an inbuilt or user-defined compressor" );
        supportsOption( "WriteImageHint=<hint>", "Export option: Hint of writing image to stream: "
                        "<IncludeData> writes Image::data() directly; "
                        "<IncludeFile> writes the image file itself to stream; "
                        "<UseExternal> writes only the filename; "
                        "<WriteOut> writes Image::data() to disk as external file." );
    }

    virtual const char* className() const { return "OpenSceneGraph Native Format Reader/Writer"; }

    Options* prepareReading( ReadResult& result, std::string& fileName, std::ios::openmode& mode, const Options* options ) const
    {
        std::string ext = osgDB::getLowerCaseFileExtension( fileName );
        if ( !acceptsExtension(ext) )
        {
            result = ReadResult::FILE_NOT_HANDLED;
            return 0;
        }
        fileName = osgDB::findDataFile( fileName, options );
        if ( fileName.empty() )
        {
            result = ReadResult::FILE_NOT_FOUND;
            return 0;
        }

        osg::ref_ptr<Options> local_opt = options ?
            static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
        local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));
        if ( ext=="osgt" ) local_opt->setOptionString( local_opt->getOptionString() + " Ascii" );
        else if ( ext=="osgx" ) local_opt->setOptionString( local_opt->getOptionString() + " XML" );
        else mode |= std::ios::binary;

        return local_opt.release();
    }

    virtual ReadResult readObject( const std::string& file, const Options* options ) const
    {
        ReadResult result = ReadResult::FILE_LOADED;
        std::string fileName = file;
        std::ios::openmode mode = std::ios::in;
        Options* local_opt = prepareReading( result, fileName, mode, options );
        if ( !result.success() ) return result;

        osgDB::ifstream istream( fileName.c_str(), mode );
        return readObject( istream, local_opt );
    }

    virtual ReadResult readObject( std::istream& fin, const Options* options ) const
    {
        osg::ref_ptr<InputIterator> ii = readInputIterator(fin, options);
        if ( !ii ) return ReadResult::FILE_NOT_HANDLED;

        InputStream is( options );

        osgDB::InputStream::ReadType readType = is.start(ii.get());
        if ( readType==InputStream::READ_UNKNOWN )
        {
            CATCH_EXCEPTION(is);
            return ReadResult::FILE_NOT_HANDLED;
        }
        is.decompress(); CATCH_EXCEPTION(is);

        osg::Object* obj = is.readObject(); CATCH_EXCEPTION(is);
        return obj;
    }

    virtual ReadResult readImage( const std::string& file, const Options* options ) const
    {
        ReadResult result = ReadResult::FILE_LOADED;
        std::string fileName = file;
        std::ios::openmode mode = std::ios::in;
        Options* local_opt = prepareReading( result, fileName, mode, options );
        if ( !result.success() ) return result;

        osgDB::ifstream istream( fileName.c_str(), mode );
        return readImage( istream, local_opt );
    }

    virtual ReadResult readImage( std::istream& fin, const Options* options ) const
    {
        osg::ref_ptr<InputIterator> ii = readInputIterator(fin, options);
        if ( !ii ) return ReadResult::FILE_NOT_HANDLED;

        InputStream is( options );
        if ( is.start(ii.get())!=InputStream::READ_IMAGE )
        {
            CATCH_EXCEPTION(is);
            return ReadResult::FILE_NOT_HANDLED;
        }
        is.decompress(); CATCH_EXCEPTION(is);
        osg::Image* image = is.readImage(); CATCH_EXCEPTION(is);
        return image;
    }

    virtual ReadResult readNode( const std::string& file, const Options* options ) const
    {
        ReadResult result = ReadResult::FILE_LOADED;
        std::string fileName = file;
        std::ios::openmode mode = std::ios::in;
        Options* local_opt = prepareReading( result, fileName, mode, options );
        if ( !result.success() ) return result;

        osgDB::ifstream istream( fileName.c_str(), mode );
        return readNode( istream, local_opt );
    }

    virtual ReadResult readNode( std::istream& fin, const Options* options ) const
    {
        osg::ref_ptr<InputIterator> ii = readInputIterator(fin, options);
        if ( !ii ) return ReadResult::FILE_NOT_HANDLED;

        InputStream is( options );
        if ( is.start(ii.get())!=InputStream::READ_SCENE )
        {
            CATCH_EXCEPTION(is);
            return ReadResult::FILE_NOT_HANDLED;
        }

        is.decompress(); CATCH_EXCEPTION(is);
        osg::Node* node = dynamic_cast<osg::Node*>(is.readObject()); CATCH_EXCEPTION(is);
        return node;
    }

    Options* prepareWriting( WriteResult& result, const std::string& fileName, std::ios::openmode& mode, const Options* options ) const
    {
        std::string ext = osgDB::getFileExtension( fileName );
        if ( !acceptsExtension(ext) ) result = WriteResult::FILE_NOT_HANDLED;

        osg::ref_ptr<Options> local_opt = options ?
            static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
        local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));
        if ( ext=="osgt" ) local_opt->setOptionString( local_opt->getOptionString() + " Ascii" );
        else if ( ext=="osgx" ) local_opt->setOptionString( local_opt->getOptionString() + " XML" );
        else mode |= std::ios::binary;

        return local_opt.release();
    }

    virtual WriteResult writeObject( const osg::Object& object, const std::string& fileName, const Options* options ) const
    {
        WriteResult result = WriteResult::FILE_SAVED;
        std::ios::openmode mode = std::ios::out;
        osg::ref_ptr<Options> local_opt = prepareWriting( result, fileName, mode, options );
        if ( !result.success() ) return result;

        osgDB::ofstream fout( fileName.c_str(), mode );
        if ( !fout ) return WriteResult::ERROR_IN_WRITING_FILE;

        result = writeObject( object, fout, local_opt.get() );
        fout.close();
        return result;
    }

    virtual WriteResult writeObject( const osg::Object& object, std::ostream& fout, const Options* options ) const
    {
        osg::ref_ptr<OutputIterator> oi = writeOutputIterator(fout, options);

        OutputStream os( options );
        os.start( oi.get(), OutputStream::WRITE_OBJECT ); CATCH_EXCEPTION(os);
        os.writeObject( &object ); CATCH_EXCEPTION(os);
        os.compress( &fout ); CATCH_EXCEPTION(os);

        oi->flush();
        if ( !os.getSchemaName().empty() )
        {
            osgDB::ofstream schemaStream( os.getSchemaName().c_str(), std::ios::out );
            if ( !schemaStream.fail() ) os.writeSchema( schemaStream );
            schemaStream.close();
        }

        if ( fout.fail() ) return WriteResult::ERROR_IN_WRITING_FILE;
        return WriteResult::FILE_SAVED;
    }

    virtual WriteResult writeImage( const osg::Image& image, const std::string& fileName, const Options* options ) const
    {
        WriteResult result = WriteResult::FILE_SAVED;
        std::ios::openmode mode = std::ios::out;
        osg::ref_ptr<Options> local_opt = prepareWriting( result, fileName, mode, options );
        if ( !result.success() ) return result;

        osgDB::ofstream fout( fileName.c_str(), mode );
        if ( !fout ) return WriteResult::ERROR_IN_WRITING_FILE;

        result = writeImage( image, fout, local_opt.get() );
        fout.close();
        return result;
    }

    virtual WriteResult writeImage( const osg::Image& image, std::ostream& fout, const Options* options ) const
    {
        osg::ref_ptr<OutputIterator> oi = writeOutputIterator(fout, options);

        OutputStream os( options );
        os.start( oi.get(), OutputStream::WRITE_IMAGE ); CATCH_EXCEPTION(os);
        os.writeImage( &image ); CATCH_EXCEPTION(os);
        os.compress( &fout ); CATCH_EXCEPTION(os);

        oi->flush();
        if ( !os.getSchemaName().empty() )
        {
            osgDB::ofstream schemaStream( os.getSchemaName().c_str(), std::ios::out );
            if ( !schemaStream.fail() ) os.writeSchema( schemaStream );
            schemaStream.close();
        }

        if ( fout.fail() ) return WriteResult::ERROR_IN_WRITING_FILE;
        return WriteResult::FILE_SAVED;
    }

    virtual WriteResult writeNode( const osg::Node& node, const std::string& fileName, const Options* options ) const
    {
        WriteResult result = WriteResult::FILE_SAVED;
        std::ios::openmode mode = std::ios::out;
        osg::ref_ptr<Options> local_opt = prepareWriting( result, fileName, mode, options );
        if ( !result.success() ) return result;

        osgDB::ofstream fout( fileName.c_str(), mode );
        if ( !fout ) return WriteResult::ERROR_IN_WRITING_FILE;

        result = writeNode( node, fout, local_opt.get() );
        fout.close();
        return result;
    }

    virtual WriteResult writeNode( const osg::Node& node, std::ostream& fout, const Options* options ) const
    {
        osg::ref_ptr<OutputIterator> oi = writeOutputIterator(fout, options);

        OutputStream os( options );
        os.start( oi.get(), OutputStream::WRITE_SCENE ); CATCH_EXCEPTION(os);
        os.writeObject( &node ); CATCH_EXCEPTION(os);
        os.compress( &fout ); CATCH_EXCEPTION(os);

        oi->flush();
        if ( !os.getSchemaName().empty() )
        {
            osgDB::ofstream schemaStream( os.getSchemaName().c_str(), std::ios::out );
            if ( !schemaStream.fail() ) os.writeSchema( schemaStream );
            schemaStream.close();
        }

        if ( fout.fail() ) return WriteResult::ERROR_IN_WRITING_FILE;
        return WriteResult::FILE_SAVED;
    }
};

REGISTER_OSGPLUGIN( osg2, ReaderWriterOSG2 )
<|endoftext|>


<commit_before>

#include <unistd.h>
#include <sstream>
#include <execinfo.h>

#include "daemon/beanstalk.hpp"
#include "video/logging_videobuffer.h"
#include "daemon/daemonconfig.h"

#include "tclap/CmdLine.h"
#include "alpr.h"
#include "openalpr/cjson.h"
#include "support/tinythread.h"
#include <curl/curl.h>
#include "support/timing.h"

#include <log4cplus/logger.h>
#include <log4cplus/loggingmacros.h>
#include <log4cplus/configurator.h>
#include <log4cplus/consoleappender.h>
#include <log4cplus/fileappender.h>

using namespace alpr;

// prototypes
void streamRecognitionThread(void* arg);
bool writeToQueue(std::string jsonResult);
bool uploadPost(CURL* curl, std::string url, std::string data);
void dataUploadThread(void* arg);

// Constants
const std::string ALPRD_CONFIG_FILE_NAME="alprd.conf";
const std::string OPENALPR_CONFIG_FILE_NAME="openalpr.conf";
const std::string DEFAULT_LOG_FILE_PATH="/var/log/alprd.log";

const std::string BEANSTALK_QUEUE_HOST="127.0.0.1";
const int BEANSTALK_PORT=11300;
const std::string BEANSTALK_TUBE_NAME="alprd";

struct CaptureThreadData
{
  std::string company_id;
  std::string stream_url;
  std::string site_id;
  int camera_id;

  bool clock_on;

  std::string config_file;
  std::string country_code;
  std::string pattern;
  bool output_images;
  std::string output_image_folder;
  int top_n;
};

struct UploadThreadData
{
  std::string upload_url;
};

void segfault_handler(int sig) {
  void *array[10];
  size_t size;

  // get void*'s for all entries on the stack
  size = backtrace(array, 10);

  // print out all the frames to stderr
  backtrace_symbols_fd(array, size, STDERR_FILENO);
  exit(1);
}

bool daemon_active;

static log4cplus::Logger logger;

int main( int argc, const char** argv )
{
  signal(SIGSEGV, segfault_handler);   // install our segfault handler
  daemon_active = true;

  bool noDaemon = false;
  bool clockOn = false;
  std::string logFile;

  std::string configDir;

  TCLAP::CmdLine cmd("OpenAlpr Daemon", ' ', Alpr::getVersion());

  TCLAP::ValueArg<std::string> configDirArg("","config","Path to the openalpr config directory that contains alprd.conf and openalpr.conf. (Default: /etc/openalpr/)",false, "/etc/openalpr/" ,"config_file");
  TCLAP::ValueArg<std::string> logFileArg("l","log","Log file to write to.  Default=" + DEFAULT_LOG_FILE_PATH,false, DEFAULT_LOG_FILE_PATH ,"topN");

  TCLAP::SwitchArg daemonOffSwitch("f","foreground","Set this flag for debugging.  Disables forking the process as a daemon and runs in the foreground.  Default=off", cmd, false);
  TCLAP::SwitchArg clockSwitch("","clock","Display timing information to log.  Default=off", cmd, false);

  try
  {

    cmd.add( configDirArg );
    cmd.add( logFileArg );

    if (cmd.parse( argc, argv ) == false)
    {
      // Error occurred while parsing.  Exit now.
      return 1;
    }

    // Make sure configDir ends in a slash
    configDir = configDirArg.getValue();
    if (hasEnding(configDir, "/") == false)
      configDir = configDir + "/";

    logFile = logFileArg.getValue();
    noDaemon = daemonOffSwitch.getValue();
    clockOn = clockSwitch.getValue();
  }
  catch (TCLAP::ArgException &e)    // catch any exceptions
  {
    std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl;
    return 1;
  }

  std::string openAlprConfigFile = configDir + OPENALPR_CONFIG_FILE_NAME;
  std::string daemonConfigFile = configDir + ALPRD_CONFIG_FILE_NAME;

  // Validate that the configuration files exist
  if (fileExists(openAlprConfigFile.c_str()) == false)
  {
    std::cerr << "error, openalpr.conf file does not exist at: " << openAlprConfigFile << std::endl;
    return 1;
  }
  if (fileExists(daemonConfigFile.c_str()) == false)
  {
    std::cerr << "error, alprd.conf file does not exist at: " << daemonConfigFile << std::endl;
    return 1;
  }

  log4cplus::BasicConfigurator config;
  config.configure();

  if (noDaemon == false)
  {
    // Fork off into a separate daemon
    daemon(0, 0);

    log4cplus::SharedAppenderPtr myAppender(new log4cplus::RollingFileAppender(logFile));
    myAppender->setName("alprd_appender");
    // Redirect std out to log file
    logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("alprd"));
    logger.addAppender(myAppender);

    LOG4CPLUS_INFO(logger, "Running OpenALPR daemon in daemon mode.");
  }
  else
  {
    //log4cplus::SharedAppenderPtr myAppender(new log4cplus::ConsoleAppender());
    //myAppender->setName("alprd_appender");
    // Redirect std out to log file
    logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("alprd"));
    //logger.addAppender(myAppender);

    LOG4CPLUS_INFO(logger, "Running OpenALPR daemon in the foreground.");
  }

  LOG4CPLUS_INFO(logger, "Using: " << daemonConfigFile << " for daemon configuration");

  std::string daemon_defaults_file = INSTALL_PREFIX  "/share/openalpr/config/alprd.defaults.conf";
  DaemonConfig daemon_config(daemonConfigFile, daemon_defaults_file);

  if (daemon_config.stream_urls.size() == 0)
  {
    LOG4CPLUS_FATAL(logger, "No video streams defined in the configuration.");
    return 1;
  }

  LOG4CPLUS_INFO(logger, "Using: " << daemon_config.imageFolder << " for storing valid plate images");

  pid_t pid;

  for (int i = 0; i < daemon_config.stream_urls.size(); i++)
  {
    pid = fork();
    if (pid == (pid_t) 0)
    {
      // This is the child process, kick off the capture data and upload threads
      CaptureThreadData* tdata = new CaptureThreadData();
      tdata->stream_url = daemon_config.stream_urls[i];
      tdata->camera_id = i + 1;
      tdata->config_file = openAlprConfigFile;
      tdata->output_images = daemon_config.storePlates;
      tdata->output_image_folder = daemon_config.imageFolder;
      tdata->country_code = daemon_config.country;
      tdata->company_id = daemon_config.company_id;
      tdata->site_id = daemon_config.site_id;
      tdata->top_n = daemon_config.topn;
      tdata->pattern = daemon_config.pattern;
      tdata->clock_on = clockOn;

      tthread::thread* thread_recognize = new tthread::thread(streamRecognitionThread, (void*) tdata);

      if (daemon_config.uploadData)
      {
	// Kick off the data upload thread
	UploadThreadData* udata = new UploadThreadData();
	udata->upload_url = daemon_config.upload_url;
	tthread::thread* thread_upload = new tthread::thread(dataUploadThread, (void*) udata );
      }

      break;
    }

    // Parent process will continue and spawn more children
  }

  while (daemon_active)
  {
    usleep(30000);
  }

}

void streamRecognitionThread(void* arg)
{
  CaptureThreadData* tdata = (CaptureThreadData*) arg;

  LOG4CPLUS_INFO(logger, "country: " << tdata->country_code << " -- config file: " << tdata->config_file );
  LOG4CPLUS_INFO(logger, "Stream " << tdata->camera_id << ": " << tdata->stream_url);
  LOG4CPLUS_INFO(logger, "Region " << tdata->pattern);

  Alpr alpr(tdata->country_code, tdata->config_file);
  alpr.setTopN(tdata->top_n);
  alpr.setDefaultRegion(tdata->pattern);

  int framenum = 0;

  LoggingVideoBuffer videoBuffer(logger);

  videoBuffer.connect(tdata->stream_url, 5);

  cv::Mat latestFrame;

  std::vector<uchar> buffer;

  LOG4CPLUS_INFO(logger, "Starting camera " << tdata->camera_id);

  while (daemon_active)
  {
    std::vector<cv::Rect> regionsOfInterest;
    int response = videoBuffer.getLatestFrame(&latestFrame, regionsOfInterest);

    if (response != -1)
    {

      timespec startTime;
      getTimeMonotonic(&startTime);

      std::vector<AlprRegionOfInterest> regionsOfInterest;
      regionsOfInterest.push_back(AlprRegionOfInterest(0,0, latestFrame.cols, latestFrame.rows));

      AlprResults results = alpr.recognize(latestFrame.data, latestFrame.elemSize(), latestFrame.cols, latestFrame.rows, regionsOfInterest);

      timespec endTime;
      getTimeMonotonic(&endTime);
      double totalProcessingTime = diffclock(startTime, endTime);

      if (tdata->clock_on)
      {
	LOG4CPLUS_INFO(logger, "Camera " << tdata->camera_id << " processed frame in: " << totalProcessingTime << " ms.");
      }

      if (results.plates.size() > 0)
      {

        std::stringstream uuid_ss;
        uuid_ss << tdata->site_id << "-cam" << tdata->camera_id << "-" << getEpochTimeMs();
	std::string uuid = uuid_ss.str();

	// Save the image to disk (using the UUID)
	if (tdata->output_images)
	{
	  std::stringstream ss;
	  ss << tdata->output_image_folder << "/" << uuid << ".jpg";

	  cv::imwrite(ss.str(), latestFrame);
	}

	// Update the JSON content to include UUID and camera ID

	std::string json = alpr.toJson(results);

	cJSON *root = cJSON_Parse(json.c_str());
	cJSON_AddStringToObject(root,	"uuid",		uuid.c_str());
	cJSON_AddNumberToObject(root,	"camera_id",	tdata->camera_id);
	cJSON_AddStringToObject(root, 	"site_id", 	tdata->site_id.c_str());
	cJSON_AddNumberToObject(root,	"img_width",	latestFrame.cols);
	cJSON_AddNumberToObject(root,	"img_height",	latestFrame.rows);

        // Add the company ID to the output if configured
        if (tdata->company_id.length() > 0)
          cJSON_AddStringToObject(root, 	"company_id", 	tdata->company_id.c_str());

	char *out;
	out=cJSON_PrintUnformatted(root);
	cJSON_Delete(root);

	std::string response(out);

	free(out);

	// Push the results to the Beanstalk queue
	for (int j = 0; j < results.plates.size(); j++)
	{
	  LOG4CPLUS_DEBUG(logger, "Writing plate " << results.plates[j].bestPlate.characters << " (" <<  uuid << ") to queue.");
	}

	writeToQueue(response);
      }
    }

    usleep(10000);
  }

  videoBuffer.disconnect();

  LOG4CPLUS_INFO(logger, "Video processing ended");

  delete tdata;
}

bool writeToQueue(std::string jsonResult)
{
  try
  {
    Beanstalk::Client client(BEANSTALK_QUEUE_HOST, BEANSTALK_PORT);
    client.use(BEANSTALK_TUBE_NAME);

    int id = client.put(jsonResult);

    if (id <= 0)
    {
      LOG4CPLUS_ERROR(logger, "Failed to write data to queue");
      return false;
    }

    LOG4CPLUS_DEBUG(logger, "put job id: " << id );

  }
  catch (const std::runtime_error& error)
  {
    LOG4CPLUS_WARN(logger, "Error connecting to Beanstalk.  Result has not been saved.");
    return false;
  }
  return true;
}

void dataUploadThread(void* arg)
{
  CURL *curl;

  /* In windows, this will init the winsock stuff */
  curl_global_init(CURL_GLOBAL_ALL);

  UploadThreadData* udata = (UploadThreadData*) arg;

  while(daemon_active)
  {
    try
    {
      /* get a curl handle */
      curl = curl_easy_init();
      Beanstalk::Client client(BEANSTALK_QUEUE_HOST, BEANSTALK_PORT);

      client.watch(BEANSTALK_TUBE_NAME);

      while (daemon_active)
      {
	Beanstalk::Job job;

	client.reserve(job);

	if (job.id() > 0)
	{
	  //LOG4CPLUS_DEBUG(logger, job.body() );
	  if (uploadPost(curl, udata->upload_url, job.body()))
	  {
	    client.del(job.id());
	    LOG4CPLUS_INFO(logger, "Job: " << job.id() << " successfully uploaded" );
	    // Wait 10ms
	    sleep_ms(10);
	  }
	  else
	  {
	    client.release(job);
	    LOG4CPLUS_WARN(logger, "Job: " << job.id() << " failed to upload.  Will retry." );
	    // Wait 2 seconds
	    sleep_ms(2000);
	  }
	}

      }

      /* always cleanup */
      curl_easy_cleanup(curl);
    }
    catch (const std::runtime_error& error)
    {
      LOG4CPLUS_WARN(logger, "Error connecting to Beanstalk.  Will retry." );
    }
    // wait 5 seconds
    usleep(5000000);
  }

  curl_global_cleanup();
}

bool uploadPost(CURL* curl, std::string url, std::string data)
{
  bool success = true;
  CURLcode res;
  struct curl_slist *headers=NULL; // init to NULL is important

  /* Add the required headers */
  headers = curl_slist_append(headers,  "Accept: application/json");
  headers = curl_slist_append( headers, "Content-Type: application/json");
  headers = curl_slist_append( headers, "charsets: utf-8");

  if(curl) {
	/* Add the headers */
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    /* First set the URL that is about to receive our POST. This URL can
       just as well be a https:// URL if that is what should receive the
       data. */
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    /* Now specify the POST data */
    //char* escaped_data = curl_easy_escape(curl, data.c_str(), data.length());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
    //curl_free(escaped_data);

    /* Perform the request, res will get the return code */
    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
    {
      success = false;
    }

  }

  return success;

}
<commit_msg>cleanup logging<commit_after>

#include <unistd.h>
#include <sstream>
#include <execinfo.h>

#include "daemon/beanstalk.hpp"
#include "video/logging_videobuffer.h"
#include "daemon/daemonconfig.h"

#include "tclap/CmdLine.h"
#include "alpr.h"
#include "openalpr/cjson.h"
#include "support/tinythread.h"
#include <curl/curl.h>
#include "support/timing.h"

#include <log4cplus/logger.h>
#include <log4cplus/loggingmacros.h>
#include <log4cplus/configurator.h>
#include <log4cplus/consoleappender.h>
#include <log4cplus/fileappender.h>

using namespace alpr;

// prototypes
void streamRecognitionThread(void* arg);
bool writeToQueue(std::string jsonResult);
bool uploadPost(CURL* curl, std::string url, std::string data);
void dataUploadThread(void* arg);

// Constants
const std::string ALPRD_CONFIG_FILE_NAME="alprd.conf";
const std::string OPENALPR_CONFIG_FILE_NAME="openalpr.conf";
const std::string DEFAULT_LOG_FILE_PATH="/var/log/alprd.log";

const std::string BEANSTALK_QUEUE_HOST="127.0.0.1";
const int BEANSTALK_PORT=11300;
const std::string BEANSTALK_TUBE_NAME="alprd";

struct CaptureThreadData
{
  std::string company_id;
  std::string stream_url;
  std::string site_id;
  int camera_id;

  bool clock_on;

  std::string config_file;
  std::string country_code;
  std::string pattern;
  bool output_images;
  std::string output_image_folder;
  int top_n;
};

struct UploadThreadData
{
  std::string upload_url;
};

void segfault_handler(int sig) {
  void *array[10];
  size_t size;

  // get void*'s for all entries on the stack
  size = backtrace(array, 10);

  // print out all the frames to stderr
  backtrace_symbols_fd(array, size, STDERR_FILENO);
  exit(1);
}

bool daemon_active;

static log4cplus::Logger logger;

int main( int argc, const char** argv )
{
  signal(SIGSEGV, segfault_handler);   // install our segfault handler
  daemon_active = true;

  bool noDaemon = false;
  bool clockOn = false;
  std::string logFile;

  std::string configDir;

  TCLAP::CmdLine cmd("OpenAlpr Daemon", ' ', Alpr::getVersion());

  TCLAP::ValueArg<std::string> configDirArg("","config","Path to the openalpr config directory that contains alprd.conf and openalpr.conf. (Default: /etc/openalpr/)",false, "/etc/openalpr/" ,"config_file");
  TCLAP::ValueArg<std::string> logFileArg("l","log","Log file to write to.  Default=" + DEFAULT_LOG_FILE_PATH,false, DEFAULT_LOG_FILE_PATH ,"topN");

  TCLAP::SwitchArg daemonOffSwitch("f","foreground","Set this flag for debugging.  Disables forking the process as a daemon and runs in the foreground.  Default=off", cmd, false);
  TCLAP::SwitchArg clockSwitch("","clock","Display timing information to log.  Default=off", cmd, false);

  try
  {

    cmd.add( configDirArg );
    cmd.add( logFileArg );

    if (cmd.parse( argc, argv ) == false)
    {
      // Error occurred while parsing.  Exit now.
      return 1;
    }

    // Make sure configDir ends in a slash
    configDir = configDirArg.getValue();
    if (hasEnding(configDir, "/") == false)
      configDir = configDir + "/";

    logFile = logFileArg.getValue();
    noDaemon = daemonOffSwitch.getValue();
    clockOn = clockSwitch.getValue();
  }
  catch (TCLAP::ArgException &e)    // catch any exceptions
  {
    std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl;
    return 1;
  }

  std::string openAlprConfigFile = configDir + OPENALPR_CONFIG_FILE_NAME;
  std::string daemonConfigFile = configDir + ALPRD_CONFIG_FILE_NAME;

  // Validate that the configuration files exist
  if (fileExists(openAlprConfigFile.c_str()) == false)
  {
    std::cerr << "error, openalpr.conf file does not exist at: " << openAlprConfigFile << std::endl;
    return 1;
  }
  if (fileExists(daemonConfigFile.c_str()) == false)
  {
    std::cerr << "error, alprd.conf file does not exist at: " << daemonConfigFile << std::endl;
    return 1;
  }

  log4cplus::BasicConfigurator config;
  config.configure();

  if (noDaemon == false)
  {
    // Fork off into a separate daemon
    daemon(0, 0);

    log4cplus::SharedAppenderPtr myAppender(new log4cplus::RollingFileAppender(logFile));
    myAppender->setName("alprd_appender");
    // Redirect std out to log file
    logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("alprd"));
    logger.addAppender(myAppender);

    LOG4CPLUS_INFO(logger, "Running OpenALPR daemon in daemon mode.");
  }
  else
  {
    //log4cplus::SharedAppenderPtr myAppender(new log4cplus::ConsoleAppender());
    //myAppender->setName("alprd_appender");
    // Redirect std out to log file
    logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("alprd"));
    //logger.addAppender(myAppender);

    LOG4CPLUS_INFO(logger, "Running OpenALPR daemon in the foreground.");
  }

  LOG4CPLUS_INFO(logger, "Using: " << daemonConfigFile << " for daemon configuration");

  std::string daemon_defaults_file = INSTALL_PREFIX  "/share/openalpr/config/alprd.defaults.conf";
  DaemonConfig daemon_config(daemonConfigFile, daemon_defaults_file);

  if (daemon_config.stream_urls.size() == 0)
  {
    LOG4CPLUS_FATAL(logger, "No video streams defined in the configuration.");
    return 1;
  }

  LOG4CPLUS_INFO(logger, "Using: " << daemon_config.imageFolder << " for storing valid plate images");

  pid_t pid;

  for (int i = 0; i < daemon_config.stream_urls.size(); i++)
  {
    pid = fork();
    if (pid == (pid_t) 0)
    {
      // This is the child process, kick off the capture data and upload threads
      CaptureThreadData* tdata = new CaptureThreadData();
      tdata->stream_url = daemon_config.stream_urls[i];
      tdata->camera_id = i + 1;
      tdata->config_file = openAlprConfigFile;
      tdata->output_images = daemon_config.storePlates;
      tdata->output_image_folder = daemon_config.imageFolder;
      tdata->country_code = daemon_config.country;
      tdata->company_id = daemon_config.company_id;
      tdata->site_id = daemon_config.site_id;
      tdata->top_n = daemon_config.topn;
      tdata->pattern = daemon_config.pattern;
      tdata->clock_on = clockOn;

      tthread::thread* thread_recognize = new tthread::thread(streamRecognitionThread, (void*) tdata);

      if (daemon_config.uploadData)
      {
	// Kick off the data upload thread
	UploadThreadData* udata = new UploadThreadData();
	udata->upload_url = daemon_config.upload_url;
	tthread::thread* thread_upload = new tthread::thread(dataUploadThread, (void*) udata );
      }

      break;
    }

    // Parent process will continue and spawn more children
  }

  while (daemon_active)
  {
    usleep(30000);
  }

}

void streamRecognitionThread(void* arg)
{
  CaptureThreadData* tdata = (CaptureThreadData*) arg;

  LOG4CPLUS_INFO(logger, "country: " << tdata->country_code << " -- config file: " << tdata->config_file );
  LOG4CPLUS_INFO(logger, "pattern: " << tdata->pattern);
  LOG4CPLUS_INFO(logger, "Stream " << tdata->camera_id << ": " << tdata->stream_url);

  Alpr alpr(tdata->country_code, tdata->config_file);
  alpr.setTopN(tdata->top_n);
  alpr.setDefaultRegion(tdata->pattern);

  int framenum = 0;

  LoggingVideoBuffer videoBuffer(logger);

  videoBuffer.connect(tdata->stream_url, 5);

  cv::Mat latestFrame;

  std::vector<uchar> buffer;

  LOG4CPLUS_INFO(logger, "Starting camera " << tdata->camera_id);

  while (daemon_active)
  {
    std::vector<cv::Rect> regionsOfInterest;
    int response = videoBuffer.getLatestFrame(&latestFrame, regionsOfInterest);

    if (response != -1)
    {

      timespec startTime;
      getTimeMonotonic(&startTime);

      std::vector<AlprRegionOfInterest> regionsOfInterest;
      regionsOfInterest.push_back(AlprRegionOfInterest(0,0, latestFrame.cols, latestFrame.rows));

      AlprResults results = alpr.recognize(latestFrame.data, latestFrame.elemSize(), latestFrame.cols, latestFrame.rows, regionsOfInterest);

      timespec endTime;
      getTimeMonotonic(&endTime);
      double totalProcessingTime = diffclock(startTime, endTime);

      if (tdata->clock_on)
      {
	LOG4CPLUS_INFO(logger, "Camera " << tdata->camera_id << " processed frame in: " << totalProcessingTime << " ms.");
      }

      if (results.plates.size() > 0)
      {

        std::stringstream uuid_ss;
        uuid_ss << tdata->site_id << "-cam" << tdata->camera_id << "-" << getEpochTimeMs();
	std::string uuid = uuid_ss.str();

	// Save the image to disk (using the UUID)
	if (tdata->output_images)
	{
	  std::stringstream ss;
	  ss << tdata->output_image_folder << "/" << uuid << ".jpg";

	  cv::imwrite(ss.str(), latestFrame);
	}

	// Update the JSON content to include UUID and camera ID

	std::string json = alpr.toJson(results);

	cJSON *root = cJSON_Parse(json.c_str());
	cJSON_AddStringToObject(root,	"uuid",		uuid.c_str());
	cJSON_AddNumberToObject(root,	"camera_id",	tdata->camera_id);
	cJSON_AddStringToObject(root, 	"site_id", 	tdata->site_id.c_str());
	cJSON_AddNumberToObject(root,	"img_width",	latestFrame.cols);
	cJSON_AddNumberToObject(root,	"img_height",	latestFrame.rows);

        // Add the company ID to the output if configured
        if (tdata->company_id.length() > 0)
          cJSON_AddStringToObject(root, 	"company_id", 	tdata->company_id.c_str());

	char *out;
	out=cJSON_PrintUnformatted(root);
	cJSON_Delete(root);

	std::string response(out);

	free(out);

	// Push the results to the Beanstalk queue
	for (int j = 0; j < results.plates.size(); j++)
	{
	  LOG4CPLUS_DEBUG(logger, "Writing plate " << results.plates[j].bestPlate.characters << " (" <<  uuid << ") to queue.");
	}

	writeToQueue(response);
      }
    }

    usleep(10000);
  }

  videoBuffer.disconnect();

  LOG4CPLUS_INFO(logger, "Video processing ended");

  delete tdata;
}

bool writeToQueue(std::string jsonResult)
{
  try
  {
    Beanstalk::Client client(BEANSTALK_QUEUE_HOST, BEANSTALK_PORT);
    client.use(BEANSTALK_TUBE_NAME);

    int id = client.put(jsonResult);

    if (id <= 0)
    {
      LOG4CPLUS_ERROR(logger, "Failed to write data to queue");
      return false;
    }

    LOG4CPLUS_DEBUG(logger, "put job id: " << id );

  }
  catch (const std::runtime_error& error)
  {
    LOG4CPLUS_WARN(logger, "Error connecting to Beanstalk.  Result has not been saved.");
    return false;
  }
  return true;
}

void dataUploadThread(void* arg)
{
  CURL *curl;

  /* In windows, this will init the winsock stuff */
  curl_global_init(CURL_GLOBAL_ALL);

  UploadThreadData* udata = (UploadThreadData*) arg;

  while(daemon_active)
  {
    try
    {
      /* get a curl handle */
      curl = curl_easy_init();
      Beanstalk::Client client(BEANSTALK_QUEUE_HOST, BEANSTALK_PORT);

      client.watch(BEANSTALK_TUBE_NAME);

      while (daemon_active)
      {
	Beanstalk::Job job;

	client.reserve(job);

	if (job.id() > 0)
	{
	  //LOG4CPLUS_DEBUG(logger, job.body() );
	  if (uploadPost(curl, udata->upload_url, job.body()))
	  {
	    client.del(job.id());
	    LOG4CPLUS_INFO(logger, "Job: " << job.id() << " successfully uploaded" );
	    // Wait 10ms
	    sleep_ms(10);
	  }
	  else
	  {
	    client.release(job);
	    LOG4CPLUS_WARN(logger, "Job: " << job.id() << " failed to upload.  Will retry." );
	    // Wait 2 seconds
	    sleep_ms(2000);
	  }
	}

      }

      /* always cleanup */
      curl_easy_cleanup(curl);
    }
    catch (const std::runtime_error& error)
    {
      LOG4CPLUS_WARN(logger, "Error connecting to Beanstalk.  Will retry." );
    }
    // wait 5 seconds
    usleep(5000000);
  }

  curl_global_cleanup();
}

bool uploadPost(CURL* curl, std::string url, std::string data)
{
  bool success = true;
  CURLcode res;
  struct curl_slist *headers=NULL; // init to NULL is important

  /* Add the required headers */
  headers = curl_slist_append(headers,  "Accept: application/json");
  headers = curl_slist_append( headers, "Content-Type: application/json");
  headers = curl_slist_append( headers, "charsets: utf-8");

  if(curl) {
	/* Add the headers */
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    /* First set the URL that is about to receive our POST. This URL can
       just as well be a https:// URL if that is what should receive the
       data. */
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    /* Now specify the POST data */
    //char* escaped_data = curl_easy_escape(curl, data.c_str(), data.length());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
    //curl_free(escaped_data);

    /* Perform the request, res will get the return code */
    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
    {
      success = false;
    }

  }

  return success;

}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* libcdr
 * Version: MPL 1.1 / GPLv2+ / LGPLv2+
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License or as specified alternatively below. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Major Contributor(s):
 * Copyright (C) 2012 Fridrich Strba <fridrich.strba@bluewin.ch>
 * Copyright (C) 2011 Eilidh McAdam <tibbylickle@gmail.com>
 *
 *
 * All Rights Reserved.
 *
 * For minor contributions see the git repository.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPLv2+"), or
 * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
 * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable
 * instead of those above.
 */

#include "CDRTransforms.h"
#include "CDRPath.h"
#include "libcdr_utils.h"

libcdr::CDRTransform::CDRTransform()
  : m_v0(1.0), m_v1(0.0), m_x0(0.0),
    m_v3(0.0), m_v4(1.0), m_y0(0.0)
{
}

libcdr::CDRTransform::CDRTransform(double v0, double v1, double x0, double v3, double v4, double y0)
  : m_v0(v0), m_v1(v1), m_x0(x0), m_v3(v3), m_v4(v4), m_y0(y0)
{
}

libcdr::CDRTransform::CDRTransform(const CDRTransform &trafo)
  : m_v0(trafo.m_v0), m_v1(trafo.m_v1), m_x0(trafo.m_x0),
    m_v3(trafo.m_v3), m_v4(trafo.m_v4), m_y0(trafo.m_y0) {}

void libcdr::CDRTransform::applyToPoint(double &x, double &y) const
{
  double tmpX = m_v0*x + m_v1*y+m_x0;
  y = m_v3*x + m_v4*y+m_y0;
  x = tmpX;
}

void libcdr::CDRTransform::applyToArc(double &rx, double &ry, double &rotation, bool &sweep, double &endx, double &endy) const
{
  // Transform the end-point, which is the easiest
  applyToPoint(endx, endy);

  // Determine whether sweep should change
  double determinant = m_v0*m_v4 - m_v1*m_v3;
  if (determinant < 0.0)
    sweep = !sweep;

  // Transform a centered ellipse

  // rx = 0 and ry = 0
  if (CDR_ALMOST_ZERO(rx) && CDR_ALMOST_ZERO(ry))
  {
    rotation = rx = ry = 0.0;
    return;
  }

  double c = cos(rotation);
  double s = sin(rotation);

  // rx > 0, ry = 0
  if (CDR_ALMOST_ZERO(ry))
  {
    double x = m_v0*c + m_v1*s;
    double y = m_v3*c + m_v4*s;
    rx *= sqrt(x*x + y*y);
    if (CDR_ALMOST_ZERO(rx))
    {
      rotation = rx = ry = 0;
      return;
    }
    rotation = atan2(y, x);
    return;
  }

  // rx = 0, ry > 0
  if (CDR_ALMOST_ZERO(rx))
  {
    double x = -m_v0*s + m_v1*c;
    double y = -m_v3*s + m_v4*c;
    ry *= sqrt(x*x + y*y);
    if (CDR_ALMOST_ZERO(ry))
    {
      rotation = rx = ry = 0;
      return;
    }
    rotation = atan2(y, x) - M_PI/2.0;
    return;
  }

  double v0, v1, v2, v3;
  if (!CDR_ALMOST_ZERO(determinant))
  {
    v0 = ry*(m_v4*c - m_v3*s);
    v1 = ry*(m_v0*s - m_v1*c);
    v2 = -rx*(m_v4*s + m_v3*c);
    v3 = rx*(m_v1*s + m_v0*c);

    // Transformed ellipse
    double A = v0*v0 + v2*v2;
    double B = 2.0*(v0*v1 + v2*v3);
    double C = v1*v1 + v3*v3;

    // Rotate the transformed ellipse
    if (CDR_ALMOST_ZERO(B))
      rotation = 0;
    else
    {
      rotation = atan2(B, A-C) / 2.0;
      c = cos(rotation);
      s = sin(rotation);
      double cc = c*c;
      double ss = s*s;
      double sc = B*s*c;
      B = A;
      A = B*cc + sc + C*ss;
      C = B*ss - sc + C*cc;
    }

    // Compute rx and ry from A and C
    if (!CDR_ALMOST_ZERO(A) && !CDR_ALMOST_ZERO(C))
    {
      double abdet = fabs(rx*ry*determinant);
      A = sqrt(fabs(A));
      C = sqrt(fabs(C));
      rx = abdet / A;
      ry = abdet / C;
      return;
    }
  }

  // Special case of a close to singular transformation
  v0 = ry*(m_v4*c - m_v3*s);
  v1 = ry*(m_v1*c - m_v0*s);
  v2 = rx*(m_v3*c + m_v4*s);
  v3 = rx*(m_v0*c + m_v1*s);

  // The result of transformation is a point
  if (CDR_ALMOST_ZERO(v3*v3 + v1*v1) && CDR_ALMOST_ZERO(v2*v2 + v0*v0))
  {
    rotation = rx = ry = 0;
    return;
  }
  else // The result of transformation is a line
  {
    double x = sqrt(v3*v3 + v1*v1);
    double y = sqrt(v2*v2 + v0*v0);
    if (v3*v3 + v1*v1 >= v2*v2 + v0*v0)
      y = (v2*v2 + v0*v0) / x;
    else
      x = (v3*v3 + v1*v1) / y;
    rx = sqrt(x*x + y*y);
    ry = 0;
    rotation = atan2(y, x);
  }
}

double libcdr::CDRTransform::_getScaleX() const
{
  double x0 = 0.0;
  double x1 = 1.0;
  double y0 = 0.0;
  double y1 = 0.0;
  applyToPoint(x0, y0);
  applyToPoint(x1, y1);
  return x1 - x0;
}

double libcdr::CDRTransform::getScaleX() const
{
  return fabs(_getScaleX());
}

bool libcdr::CDRTransform::getFlipX() const
{
  return (0 > _getScaleX());
}

double libcdr::CDRTransform::_getScaleY() const
{
  double x0 = 0.0;
  double x1 = 0.0;
  double y0 = 0.0;
  double y1 = 1.0;
  applyToPoint(x0, y0);
  applyToPoint(x1, y1);
  return y1 - y0;
}

double libcdr::CDRTransform::getScaleY() const
{
  return fabs(_getScaleY());
}

bool libcdr::CDRTransform::getFlipY() const
{
  return (0 > _getScaleY());
}

double libcdr::CDRTransform::getTranslateX() const
{
  double x = 0.0;
  double y = 0.0;
  applyToPoint(x, y);
  return x;
}

double libcdr::CDRTransform::getTranslateY() const
{
  double x = 0.0;
  double y = 0.0;
  applyToPoint(x, y);
  return y;
}

libcdr::CDRTransforms::CDRTransforms()
  : m_trafos()
{
}

libcdr::CDRTransforms::CDRTransforms(const CDRTransforms &trafos)
  : m_trafos(trafos.m_trafos)
{
}

libcdr::CDRTransforms::~CDRTransforms()
{
}

void libcdr::CDRTransforms::append(double v0, double v1, double x0, double v3, double v4, double y0)
{
  append(CDRTransform(v0, v1, x0, v3, v4, y0));
}

void libcdr::CDRTransforms::append(const CDRTransform &trafo)
{
  m_trafos.push_back(trafo);
}

void libcdr::CDRTransforms::clear()
{
  m_trafos.clear();
}

bool libcdr::CDRTransforms::empty() const
{
  return m_trafos.empty();
}

void libcdr::CDRTransforms::applyToPoint(double &x, double &y) const
{
  for (std::vector<CDRTransform>::const_iterator iter = m_trafos.begin(); iter != m_trafos.end(); ++iter)
    iter->applyToPoint(x,y);
}

void libcdr::CDRTransforms::applyToArc(double &rx, double &ry, double &rotation, bool &sweep, double &x, double &y) const
{
  for (std::vector<CDRTransform>::const_iterator iter = m_trafos.begin(); iter != m_trafos.end(); ++iter)
    iter->applyToArc(rx, ry, rotation, sweep, x, y);
}

double libcdr::CDRTransforms::_getScaleX() const
{
  double x0 = 0.0;
  double x1 = 1.0;
  double y0 = 0.0;
  double y1 = 0.0;
  applyToPoint(x0, y0);
  applyToPoint(x1, y1);
  return x1 - x0;
}

double libcdr::CDRTransforms::getScaleX() const
{
  return fabs(_getScaleX());
}

bool libcdr::CDRTransforms::getFlipX() const
{
  return (0 > _getScaleX());
}

double libcdr::CDRTransforms::_getScaleY() const
{
  double x0 = 0.0;
  double x1 = 0.0;
  double y0 = 0.0;
  double y1 = 1.0;
  applyToPoint(x0, y0);
  applyToPoint(x1, y1);
  return y1 - y0;
}

double libcdr::CDRTransforms::getScaleY() const
{
  return fabs(_getScaleY());
}

bool libcdr::CDRTransforms::getFlipY() const
{
  return (0 > _getScaleY());
}

double libcdr::CDRTransforms::getTranslateX() const
{
  double x = 0.0;
  double y = 0.0;
  applyToPoint(x, y);
  return x;
}

double libcdr::CDRTransforms::getTranslateY() const
{
  double x = 0.0;
  double y = 0.0;
  applyToPoint(x, y);
  return y;
}

/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
<commit_msg>Reduce a scope a bit<commit_after>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* libcdr
 * Version: MPL 1.1 / GPLv2+ / LGPLv2+
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License or as specified alternatively below. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Major Contributor(s):
 * Copyright (C) 2012 Fridrich Strba <fridrich.strba@bluewin.ch>
 * Copyright (C) 2011 Eilidh McAdam <tibbylickle@gmail.com>
 *
 *
 * All Rights Reserved.
 *
 * For minor contributions see the git repository.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPLv2+"), or
 * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
 * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable
 * instead of those above.
 */

#include "CDRTransforms.h"
#include "CDRPath.h"
#include "libcdr_utils.h"

libcdr::CDRTransform::CDRTransform()
  : m_v0(1.0), m_v1(0.0), m_x0(0.0),
    m_v3(0.0), m_v4(1.0), m_y0(0.0)
{
}

libcdr::CDRTransform::CDRTransform(double v0, double v1, double x0, double v3, double v4, double y0)
  : m_v0(v0), m_v1(v1), m_x0(x0), m_v3(v3), m_v4(v4), m_y0(y0)
{
}

libcdr::CDRTransform::CDRTransform(const CDRTransform &trafo)
  : m_v0(trafo.m_v0), m_v1(trafo.m_v1), m_x0(trafo.m_x0),
    m_v3(trafo.m_v3), m_v4(trafo.m_v4), m_y0(trafo.m_y0) {}

void libcdr::CDRTransform::applyToPoint(double &x, double &y) const
{
  double tmpX = m_v0*x + m_v1*y+m_x0;
  y = m_v3*x + m_v4*y+m_y0;
  x = tmpX;
}

void libcdr::CDRTransform::applyToArc(double &rx, double &ry, double &rotation, bool &sweep, double &endx, double &endy) const
{
  // Transform the end-point, which is the easiest
  applyToPoint(endx, endy);

  // Determine whether sweep should change
  double determinant = m_v0*m_v4 - m_v1*m_v3;
  if (determinant < 0.0)
    sweep = !sweep;

  // Transform a centered ellipse

  // rx = 0 and ry = 0
  if (CDR_ALMOST_ZERO(rx) && CDR_ALMOST_ZERO(ry))
  {
    rotation = rx = ry = 0.0;
    return;
  }

  // rx > 0, ry = 0
  if (CDR_ALMOST_ZERO(ry))
  {
    double x = m_v0*cos(rotation) + m_v1*sin(rotation);
    double y = m_v3*cos(rotation) + m_v4*sin(rotation);
    rx *= sqrt(x*x + y*y);
    if (CDR_ALMOST_ZERO(rx))
    {
      rotation = rx = ry = 0;
      return;
    }
    rotation = atan2(y, x);
    return;
  }

  // rx = 0, ry > 0
  if (CDR_ALMOST_ZERO(rx))
  {
    double x = -m_v0*sin(rotation) + m_v1*cos(rotation);
    double y = -m_v3*sin(rotation) + m_v4*cos(rotation);
    ry *= sqrt(x*x + y*y);
    if (CDR_ALMOST_ZERO(ry))
    {
      rotation = rx = ry = 0;
      return;
    }
    rotation = atan2(y, x) - M_PI/2.0;
    return;
  }

  double v0, v1, v2, v3;
  if (!CDR_ALMOST_ZERO(determinant))
  {
    v0 = ry*(m_v4*cos(rotation)- m_v3*sin(rotation));
    v1 = ry*(m_v0*sin(rotation)- m_v1*cos(rotation));
    v2 = -rx*(m_v4*sin(rotation) + m_v3*cos(rotation));
    v3 = rx*(m_v1*sin(rotation) + m_v0*cos(rotation));

    // Transformed ellipse
    double A = v0*v0 + v2*v2;
    double B = 2.0*(v0*v1 + v2*v3);
    double C = v1*v1 + v3*v3;

    // Rotate the transformed ellipse
    if (CDR_ALMOST_ZERO(B))
      rotation = 0;
    else
    {
      rotation = atan2(B, A-C) / 2.0;
      double c = cos(rotation);
      double s = sin(rotation);
      double cc = c*c;
      double ss = s*s;
      double sc = B*s*c;
      B = A;
      A = B*cc + sc + C*ss;
      C = B*ss - sc + C*cc;
    }

    // Compute rx and ry from A and C
    if (!CDR_ALMOST_ZERO(A) && !CDR_ALMOST_ZERO(C))
    {
      double abdet = fabs(rx*ry*determinant);
      A = sqrt(fabs(A));
      C = sqrt(fabs(C));
      rx = abdet / A;
      ry = abdet / C;
      return;
    }
  }

  // Special case of a close to singular transformation
  v0 = ry*(m_v4*cos(rotation) - m_v3*sin(rotation));
  v1 = ry*(m_v1*cos(rotation) - m_v0*sin(rotation));
  v2 = rx*(m_v3*cos(rotation) + m_v4*sin(rotation));
  v3 = rx*(m_v0*cos(rotation) + m_v1*sin(rotation));

  // The result of transformation is a point
  if (CDR_ALMOST_ZERO(v3*v3 + v1*v1) && CDR_ALMOST_ZERO(v2*v2 + v0*v0))
  {
    rotation = rx = ry = 0;
    return;
  }
  else // The result of transformation is a line
  {
    double x = sqrt(v3*v3 + v1*v1);
    double y = sqrt(v2*v2 + v0*v0);
    if (v3*v3 + v1*v1 >= v2*v2 + v0*v0)
      y = (v2*v2 + v0*v0) / x;
    else
      x = (v3*v3 + v1*v1) / y;
    rx = sqrt(x*x + y*y);
    ry = 0;
    rotation = atan2(y, x);
  }
}

double libcdr::CDRTransform::_getScaleX() const
{
  double x0 = 0.0;
  double x1 = 1.0;
  double y0 = 0.0;
  double y1 = 0.0;
  applyToPoint(x0, y0);
  applyToPoint(x1, y1);
  return x1 - x0;
}

double libcdr::CDRTransform::getScaleX() const
{
  return fabs(_getScaleX());
}

bool libcdr::CDRTransform::getFlipX() const
{
  return (0 > _getScaleX());
}

double libcdr::CDRTransform::_getScaleY() const
{
  double x0 = 0.0;
  double x1 = 0.0;
  double y0 = 0.0;
  double y1 = 1.0;
  applyToPoint(x0, y0);
  applyToPoint(x1, y1);
  return y1 - y0;
}

double libcdr::CDRTransform::getScaleY() const
{
  return fabs(_getScaleY());
}

bool libcdr::CDRTransform::getFlipY() const
{
  return (0 > _getScaleY());
}

double libcdr::CDRTransform::getTranslateX() const
{
  double x = 0.0;
  double y = 0.0;
  applyToPoint(x, y);
  return x;
}

double libcdr::CDRTransform::getTranslateY() const
{
  double x = 0.0;
  double y = 0.0;
  applyToPoint(x, y);
  return y;
}

libcdr::CDRTransforms::CDRTransforms()
  : m_trafos()
{
}

libcdr::CDRTransforms::CDRTransforms(const CDRTransforms &trafos)
  : m_trafos(trafos.m_trafos)
{
}

libcdr::CDRTransforms::~CDRTransforms()
{
}

void libcdr::CDRTransforms::append(double v0, double v1, double x0, double v3, double v4, double y0)
{
  append(CDRTransform(v0, v1, x0, v3, v4, y0));
}

void libcdr::CDRTransforms::append(const CDRTransform &trafo)
{
  m_trafos.push_back(trafo);
}

void libcdr::CDRTransforms::clear()
{
  m_trafos.clear();
}

bool libcdr::CDRTransforms::empty() const
{
  return m_trafos.empty();
}

void libcdr::CDRTransforms::applyToPoint(double &x, double &y) const
{
  for (std::vector<CDRTransform>::const_iterator iter = m_trafos.begin(); iter != m_trafos.end(); ++iter)
    iter->applyToPoint(x,y);
}

void libcdr::CDRTransforms::applyToArc(double &rx, double &ry, double &rotation, bool &sweep, double &x, double &y) const
{
  for (std::vector<CDRTransform>::const_iterator iter = m_trafos.begin(); iter != m_trafos.end(); ++iter)
    iter->applyToArc(rx, ry, rotation, sweep, x, y);
}

double libcdr::CDRTransforms::_getScaleX() const
{
  double x0 = 0.0;
  double x1 = 1.0;
  double y0 = 0.0;
  double y1 = 0.0;
  applyToPoint(x0, y0);
  applyToPoint(x1, y1);
  return x1 - x0;
}

double libcdr::CDRTransforms::getScaleX() const
{
  return fabs(_getScaleX());
}

bool libcdr::CDRTransforms::getFlipX() const
{
  return (0 > _getScaleX());
}

double libcdr::CDRTransforms::_getScaleY() const
{
  double x0 = 0.0;
  double x1 = 0.0;
  double y0 = 0.0;
  double y1 = 1.0;
  applyToPoint(x0, y0);
  applyToPoint(x1, y1);
  return y1 - y0;
}

double libcdr::CDRTransforms::getScaleY() const
{
  return fabs(_getScaleY());
}

bool libcdr::CDRTransforms::getFlipY() const
{
  return (0 > _getScaleY());
}

double libcdr::CDRTransforms::getTranslateX() const
{
  double x = 0.0;
  double y = 0.0;
  applyToPoint(x, y);
  return x;
}

double libcdr::CDRTransforms::getTranslateY() const
{
  double x = 0.0;
  double y = 0.0;
  applyToPoint(x, y);
  return y;
}

/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
<|endoftext|>


<commit_before>/*
 * TWI/I2C library for Arduino Zero
 * Copyright (c) 2015 Arduino LLC. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

extern "C" {
#include <string.h>
}

#include <Arduino.h>
#include <wiring_private.h>

#include "Wire.h"

TwoWire::TwoWire(SERCOM * s, uint8_t pinSDA, uint8_t pinSCL)
{
  this->sercom = s;
  this->_uc_pinSDA=pinSDA;
  this->_uc_pinSCL=pinSCL;
  transmissionBegun = false;
}

void TwoWire::begin(void) {
  //Master Mode
  sercom->initMasterWIRE(TWI_CLOCK);
  sercom->enableWIRE();

  pinPeripheral(_uc_pinSDA, g_APinDescription[_uc_pinSDA].ulPinType);
  pinPeripheral(_uc_pinSCL, g_APinDescription[_uc_pinSCL].ulPinType);
}

void TwoWire::begin(uint8_t address) {
  //Slave mode
  sercom->initSlaveWIRE(address);
  sercom->enableWIRE();

  pinPeripheral(_uc_pinSDA, g_APinDescription[_uc_pinSDA].ulPinType);
  pinPeripheral(_uc_pinSCL, g_APinDescription[_uc_pinSCL].ulPinType);
}

void TwoWire::end() {
  sercom->disableWIRE();
}

void TwoWire::setClock(uint32_t frequency) {
	// dummy funtion
}

uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit)
{
  if(quantity == 0)
  {
    return 0;
  }

  size_t byteRead = 0;

  if(sercom->startTransmissionWIRE(address, WIRE_READ_FLAG))
  {
    // Read first data
    rxBuffer.store_char(sercom->readDataWIRE());

    // Connected to slave
    for (byteRead = 1; byteRead < quantity; ++byteRead)
    {
      sercom->prepareAckBitWIRE();                          // Prepare Acknowledge
      sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_READ); // Prepare the ACK command for the slave
      rxBuffer.store_char(sercom->readDataWIRE());          // Read data and send the ACK
    }
    sercom->prepareNackBitWIRE();                           // Prepare NACK to stop slave transmission
    //sercom->readDataWIRE();                               // Clear data register to send NACK
    sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);   // Send Stop
  }

  return byteRead;
}

uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity)
{
  return requestFrom(address, quantity, true);
}

void TwoWire::beginTransmission(uint8_t address) {
  // save address of target and clear buffer
  txAddress = address;
  txBuffer.clear();

  transmissionBegun = true;
}

// Errors:
//  0 : Success
//  1 : Data too long
//  2 : NACK on transmit of address
//  3 : NACK on transmit of data
//  4 : Other error
uint8_t TwoWire::endTransmission(bool stopBit)
{
  transmissionBegun = false ;

  // Check if there are data to send
  if ( txBuffer.available() == 0)
  {
    return 4 ;
  }

  // Start I2C transmission
  if ( !sercom->startTransmissionWIRE( txAddress, WIRE_WRITE_FLAG ) )
  {
    sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);
    return 2 ;  // Address error
  }

  // Send all buffer
  while( txBuffer.available() )
  {
    // Trying to send data
    if ( !sercom->sendDataMasterWIRE( txBuffer.read_char() ) )
    {
      sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);
      return 3 ;  // Nack or error
    }
  }
  sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);

  return 0;
}

uint8_t TwoWire::endTransmission()
{
  return endTransmission(true);
}

size_t TwoWire::write(uint8_t ucData)
{
  if(sercom->isMasterWIRE())
  {
    // No writing, without begun transmission or a full buffer
    if ( !transmissionBegun || txBuffer.isFull() )
    {
      return 0 ;
    }

    txBuffer.store_char( ucData ) ;

    return 1 ;
  }
  else
  {
    if(sercom->sendDataSlaveWIRE( ucData ))
    {
      return 1;
    }
  }

  return 0;
}

size_t TwoWire::write(const uint8_t *data, size_t quantity)
{
  //Try to store all data
  for(size_t i = 0; i < quantity; ++i)
  {
    //Return the number of data stored, when the buffer is full (if write return 0)
    if(!write(data[i]))
      return i;
  }

  //All data stored
  return quantity;
}

int TwoWire::available(void)
{
  return rxBuffer.available();
}

int TwoWire::read(void)
{
  return rxBuffer.read_char();
}

int TwoWire::peek(void)
{
  return rxBuffer.peek();
}

void TwoWire::flush(void)
{
  // Do nothing, use endTransmission(..) to force
  // data transfer.
}

void TwoWire::onReceive(void(*function)(int))
{
  onReceiveCallback = function;
}

void TwoWire::onRequest(void(*function)(void))
{
  onRequestCallback = function;
}

void TwoWire::onService(void)
{
  if ( sercom->isSlaveWIRE() )
  {
    if(sercom->isAddressMatch())  //Address Match
    {
      sercom->prepareAckBitWIRE();
      sercom->prepareCommandBitsWire(0x03);

      //Is a request ?
      if(sercom->isMasterReadOperationWIRE())
      {
        //Calling onRequestCallback, if exists
        if(onRequestCallback)
        {
          onRequestCallback();
        }
      }
    }
    else if(sercom->isDataReadyWIRE()) //Received data
    {
      if (rxBuffer.isFull()) {
        sercom->prepareNackBitWIRE();
      } else {
        //Store data
        rxBuffer.store_char(sercom->readDataWIRE());

        sercom->prepareAckBitWIRE();
      }

      sercom->prepareCommandBitsWire(0x03);
    }
    else if(sercom->isStopDetectedWIRE() || sercom->isRestartDetectedWIRE()) //Stop or Restart detected
    {
      sercom->prepareAckBitWIRE();
      sercom->prepareCommandBitsWire(0x03);

      //Calling onReceiveCallback, if exists
      if(onReceiveCallback)
      {
        onReceiveCallback(available());
      }

      rxBuffer.clear();
    }
  }
}

#if WIRE_INTERFACES_COUNT > 0

/* In case new variant doesn't define these macros,
 * we put here the ones for Arduino Zero.
 *
 * These values should be different on some variants!
 */

#ifndef PERIPH_WIRE
#  define PERIPH_WIRE          sercom3
#  define WIRE_IT_HANDLER      SERCOM3_Handler
#endif // PERIPH_WIRE

TwoWire Wire(&PERIPH_WIRE, PIN_WIRE_SDA, PIN_WIRE_SCL);

void WIRE_IT_HANDLER(void) {
  Wire.onService();
}

#endif // WIRE_INTERFACES_COUNT > 0
<commit_msg>Wire slave receiver with repeated starts working<commit_after>/*
 * TWI/I2C library for Arduino Zero
 * Copyright (c) 2015 Arduino LLC. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

extern "C" {
#include <string.h>
}

#include <Arduino.h>
#include <wiring_private.h>

#include "Wire.h"

TwoWire::TwoWire(SERCOM * s, uint8_t pinSDA, uint8_t pinSCL)
{
  this->sercom = s;
  this->_uc_pinSDA=pinSDA;
  this->_uc_pinSCL=pinSCL;
  transmissionBegun = false;
}

void TwoWire::begin(void) {
  //Master Mode
  sercom->initMasterWIRE(TWI_CLOCK);
  sercom->enableWIRE();

  pinPeripheral(_uc_pinSDA, g_APinDescription[_uc_pinSDA].ulPinType);
  pinPeripheral(_uc_pinSCL, g_APinDescription[_uc_pinSCL].ulPinType);
}

void TwoWire::begin(uint8_t address) {
  //Slave mode
  sercom->initSlaveWIRE(address);
  sercom->enableWIRE();

  pinPeripheral(_uc_pinSDA, g_APinDescription[_uc_pinSDA].ulPinType);
  pinPeripheral(_uc_pinSCL, g_APinDescription[_uc_pinSCL].ulPinType);
}

void TwoWire::end() {
  sercom->disableWIRE();
}

void TwoWire::setClock(uint32_t frequency) {
	// dummy funtion
}

uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit)
{
  if(quantity == 0)
  {
    return 0;
  }

  size_t byteRead = 0;

  if(sercom->startTransmissionWIRE(address, WIRE_READ_FLAG))
  {
    // Read first data
    rxBuffer.store_char(sercom->readDataWIRE());

    // Connected to slave
    for (byteRead = 1; byteRead < quantity; ++byteRead)
    {
      sercom->prepareAckBitWIRE();                          // Prepare Acknowledge
      sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_READ); // Prepare the ACK command for the slave
      rxBuffer.store_char(sercom->readDataWIRE());          // Read data and send the ACK
    }
    sercom->prepareNackBitWIRE();                           // Prepare NACK to stop slave transmission
    //sercom->readDataWIRE();                               // Clear data register to send NACK
    sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);   // Send Stop
  }

  return byteRead;
}

uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity)
{
  return requestFrom(address, quantity, true);
}

void TwoWire::beginTransmission(uint8_t address) {
  // save address of target and clear buffer
  txAddress = address;
  txBuffer.clear();

  transmissionBegun = true;
}

// Errors:
//  0 : Success
//  1 : Data too long
//  2 : NACK on transmit of address
//  3 : NACK on transmit of data
//  4 : Other error
uint8_t TwoWire::endTransmission(bool stopBit)
{
  transmissionBegun = false ;

  // Check if there are data to send
  if ( txBuffer.available() == 0)
  {
    return 4 ;
  }

  // Start I2C transmission
  if ( !sercom->startTransmissionWIRE( txAddress, WIRE_WRITE_FLAG ) )
  {
    sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);
    return 2 ;  // Address error
  }

  // Send all buffer
  while( txBuffer.available() )
  {
    // Trying to send data
    if ( !sercom->sendDataMasterWIRE( txBuffer.read_char() ) )
    {
      sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);
      return 3 ;  // Nack or error
    }
  }
  sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);

  return 0;
}

uint8_t TwoWire::endTransmission()
{
  return endTransmission(true);
}

size_t TwoWire::write(uint8_t ucData)
{
  if(sercom->isMasterWIRE())
  {
    // No writing, without begun transmission or a full buffer
    if ( !transmissionBegun || txBuffer.isFull() )
    {
      return 0 ;
    }

    txBuffer.store_char( ucData ) ;

    return 1 ;
  }
  else
  {
    if(sercom->sendDataSlaveWIRE( ucData ))
    {
      return 1;
    }
  }

  return 0;
}

size_t TwoWire::write(const uint8_t *data, size_t quantity)
{
  //Try to store all data
  for(size_t i = 0; i < quantity; ++i)
  {
    //Return the number of data stored, when the buffer is full (if write return 0)
    if(!write(data[i]))
      return i;
  }

  //All data stored
  return quantity;
}

int TwoWire::available(void)
{
  return rxBuffer.available();
}

int TwoWire::read(void)
{
  return rxBuffer.read_char();
}

int TwoWire::peek(void)
{
  return rxBuffer.peek();
}

void TwoWire::flush(void)
{
  // Do nothing, use endTransmission(..) to force
  // data transfer.
}

void TwoWire::onReceive(void(*function)(int))
{
  onReceiveCallback = function;
}

void TwoWire::onRequest(void(*function)(void))
{
  onRequestCallback = function;
}

void TwoWire::onService(void)
{
  if ( sercom->isSlaveWIRE() )
  {
    if(sercom->isStopDetectedWIRE() ||
        (sercom->isAddressMatch() && sercom->isRestartDetectedWIRE())) //Stop or Restart detected
    {
      sercom->prepareAckBitWIRE();
      sercom->prepareCommandBitsWire(0x03);

      //Calling onReceiveCallback, if exists
      if(onReceiveCallback)
      {
        onReceiveCallback(available());
      }

      rxBuffer.clear();
    }
    else if(sercom->isAddressMatch())  //Address Match
    {
      sercom->prepareAckBitWIRE();
      sercom->prepareCommandBitsWire(0x03);

      if(sercom->isMasterReadOperationWIRE()) //Is a request ?
      {
        //Calling onRequestCallback, if exists
        if(onRequestCallback)
        {
          onRequestCallback();
        }
      }
    }
    else if(sercom->isDataReadyWIRE()) //Received data
    {
      if (rxBuffer.isFull()) {
        sercom->prepareNackBitWIRE();
      } else {
        //Store data
        rxBuffer.store_char(sercom->readDataWIRE());

        sercom->prepareAckBitWIRE();
      }

      sercom->prepareCommandBitsWire(0x03);
    }
  }
}

#if WIRE_INTERFACES_COUNT > 0

/* In case new variant doesn't define these macros,
 * we put here the ones for Arduino Zero.
 *
 * These values should be different on some variants!
 */

#ifndef PERIPH_WIRE
#  define PERIPH_WIRE          sercom3
#  define WIRE_IT_HANDLER      SERCOM3_Handler
#endif // PERIPH_WIRE

TwoWire Wire(&PERIPH_WIRE, PIN_WIRE_SDA, PIN_WIRE_SCL);

void WIRE_IT_HANDLER(void) {
  Wire.onService();
}

#endif // WIRE_INTERFACES_COUNT > 0
<|endoftext|>


<commit_before>/*=========================================================================

 Program:   ORFEO Toolbox
 Language:  C++
 Date:      $Date$
 Version:   $Revision$

 Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
 See OTBCopyright.txt for details.

 This software is distributed WITHOUT ANY WARRANTY; without even
 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 PURPOSE.  See the above copyright notices for more information.

 =========================================================================*/
#include "otbSharkImageClassificationFilter.h"
#include "otbVectorImage.h"
#include "otbImage.h"
#include "otbImageFileReader.h"
#include "otbImageFileWriter.h"
#include "otbSharkRandomForestsMachineLearningModelFactory.h"
#include <random>
#include <chrono>

const unsigned int Dimension = 2;
typedef float PixelType;
typedef unsigned short LabeledPixelType;

typedef otb::VectorImage<PixelType, Dimension> ImageType;
typedef otb::Image<LabeledPixelType, Dimension> LabeledImageType;
typedef otb::SharkImageClassificationFilter<ImageType, LabeledImageType> ClassificationFilterType;
typedef ClassificationFilterType::ModelType ModelType;
typedef ClassificationFilterType::ValueType ValueType;
typedef ClassificationFilterType::LabelType LabelType;
typedef otb::SharkRandomForestsMachineLearningModelFactory<ValueType, LabelType> MachineLearningModelFactoryType;
typedef otb::ImageFileReader<ImageType> ReaderType;
typedef otb::ImageFileWriter<LabeledImageType> WriterType;

typedef otb::SharkRandomForestsMachineLearningModel<PixelType,short unsigned int>         MachineLearningModelType;
typedef MachineLearningModelType::InputValueType       InputValueType;
typedef MachineLearningModelType::InputSampleType      InputSampleType;
typedef MachineLearningModelType::InputListSampleType  InputListSampleType;
typedef MachineLearningModelType::TargetValueType      TargetValueType;
typedef MachineLearningModelType::TargetSampleType     TargetSampleType;
typedef MachineLearningModelType::TargetListSampleType TargetListSampleType;

void generateSamples(unsigned int num_classes, unsigned int num_samples,
                     unsigned int num_features,
                     InputListSampleType * samples,
                     TargetListSampleType * labels)
{
  std::default_random_engine generator;
  std::uniform_int_distribution<int> label_distribution(1,num_classes);
  std::uniform_int_distribution<int> feat_distribution(0,256);
  for(size_t scount=0; scount<num_samples; ++scount)
    {
    LabeledPixelType label = label_distribution(generator);
    InputSampleType sample(num_features);
    for(auto i=0; i<num_features; ++i)
      sample[i]= feat_distribution(generator);
    samples->SetMeasurementVectorSize(num_features);
    samples->PushBack(sample);
    labels->PushBack(label);
    }
}

void buildModel(unsigned int num_classes, unsigned int num_samples,
                unsigned int num_features, std::string modelfname)
{
  InputListSampleType::Pointer samples = InputListSampleType::New();
  TargetListSampleType::Pointer labels = TargetListSampleType::New();

  std::cout << "Sample generation\n";
  generateSamples(num_classes, num_samples, num_features, samples, labels);

  MachineLearningModelType::Pointer classifier = MachineLearningModelType::New();
  classifier->SetInputListSample(samples);
  classifier->SetTargetListSample(labels);
  classifier->SetRegressionMode(false);
  classifier->SetNumberOfTrees(100);
  classifier->SetMTry(0);
  classifier->SetNodeSize(25);
  classifier->SetOobRatio(0.3);
  std::cout << "Training\n";
  classifier->Train();
  classifier->Save(modelfname);
}

int otbSharkImageClassificationFilter(int argc, char * argv[])
{
  if(argc<4 || argc>5)
    {
    std::cout << "Usage: input_image output_imae batchmode [in_model_name]\n";
    }
  std::string imfname = argv[1];
  std::string outfname = argv[2];
  bool batch = (std::string(argv[3])=="1");
  std::string modelfname = "/tmp/rf_model.txt";

  ReaderType::Pointer reader = ReaderType::New();
  reader->SetFileName(imfname);
  reader->UpdateOutputInformation();

  auto num_features = reader->GetOutput()->GetNumberOfComponentsPerPixel();

  std::cout << "Image has " << num_features << " bands\n";

  if(argc==5)
    {
    modelfname = argv[4];
    }
  else
    {
    buildModel(3, 1000, num_features, modelfname);
    }

  ClassificationFilterType::Pointer filter = ClassificationFilterType::New();

  MachineLearningModelType::Pointer model = MachineLearningModelType::New();
  model->Load(modelfname);
  filter->SetModel(model);
  filter->SetInput(reader->GetOutput());

  WriterType::Pointer writer = WriterType::New();
  writer->SetInput(filter->GetOutput());
  writer->SetFileName(outfname);
  std::cout << "Classification\n";
  filter->SetBatch(batch);
  using TimeT = std::chrono::milliseconds;
  auto start = std::chrono::system_clock::now();
  writer->Update();
  auto duration = std::chrono::duration_cast< TimeT>
    (std::chrono::system_clock::now() - start);
  auto elapsed = duration.count();
  std::cout << "Classification took " << elapsed << " ms\n";

  return EXIT_SUCCESS;
}
<commit_msg>TEST: time probe for training shark RF<commit_after>/*=========================================================================

 Program:   ORFEO Toolbox
 Language:  C++
 Date:      $Date$
 Version:   $Revision$

 Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
 See OTBCopyright.txt for details.

 This software is distributed WITHOUT ANY WARRANTY; without even
 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 PURPOSE.  See the above copyright notices for more information.

 =========================================================================*/
#include "otbSharkImageClassificationFilter.h"
#include "otbVectorImage.h"
#include "otbImage.h"
#include "otbImageFileReader.h"
#include "otbImageFileWriter.h"
#include "otbSharkRandomForestsMachineLearningModelFactory.h"
#include <random>
#include <chrono>

const unsigned int Dimension = 2;
typedef float PixelType;
typedef unsigned short LabeledPixelType;

typedef otb::VectorImage<PixelType, Dimension> ImageType;
typedef otb::Image<LabeledPixelType, Dimension> LabeledImageType;
typedef otb::SharkImageClassificationFilter<ImageType, LabeledImageType> ClassificationFilterType;
typedef ClassificationFilterType::ModelType ModelType;
typedef ClassificationFilterType::ValueType ValueType;
typedef ClassificationFilterType::LabelType LabelType;
typedef otb::SharkRandomForestsMachineLearningModelFactory<ValueType, LabelType> MachineLearningModelFactoryType;
typedef otb::ImageFileReader<ImageType> ReaderType;
typedef otb::ImageFileWriter<LabeledImageType> WriterType;

typedef otb::SharkRandomForestsMachineLearningModel<PixelType,short unsigned int>         MachineLearningModelType;
typedef MachineLearningModelType::InputValueType       InputValueType;
typedef MachineLearningModelType::InputSampleType      InputSampleType;
typedef MachineLearningModelType::InputListSampleType  InputListSampleType;
typedef MachineLearningModelType::TargetValueType      TargetValueType;
typedef MachineLearningModelType::TargetSampleType     TargetSampleType;
typedef MachineLearningModelType::TargetListSampleType TargetListSampleType;

void generateSamples(unsigned int num_classes, unsigned int num_samples,
                     unsigned int num_features,
                     InputListSampleType * samples,
                     TargetListSampleType * labels)
{
  std::default_random_engine generator;
  std::uniform_int_distribution<int> label_distribution(1,num_classes);
  std::uniform_int_distribution<int> feat_distribution(0,256);
  for(size_t scount=0; scount<num_samples; ++scount)
    {
    LabeledPixelType label = label_distribution(generator);
    InputSampleType sample(num_features);
    for(auto i=0; i<num_features; ++i)
      sample[i]= feat_distribution(generator);
    samples->SetMeasurementVectorSize(num_features);
    samples->PushBack(sample);
    labels->PushBack(label);
    }
}

void buildModel(unsigned int num_classes, unsigned int num_samples,
                unsigned int num_features, std::string modelfname)
{
  InputListSampleType::Pointer samples = InputListSampleType::New();
  TargetListSampleType::Pointer labels = TargetListSampleType::New();

  std::cout << "Sample generation\n";
  generateSamples(num_classes, num_samples, num_features, samples, labels);

  MachineLearningModelType::Pointer classifier = MachineLearningModelType::New();
  classifier->SetInputListSample(samples);
  classifier->SetTargetListSample(labels);
  classifier->SetRegressionMode(false);
  classifier->SetNumberOfTrees(100);
  classifier->SetMTry(0);
  classifier->SetNodeSize(25);
  classifier->SetOobRatio(0.3);
  std::cout << "Training\n";
  using TimeT = std::chrono::milliseconds;
  auto start = std::chrono::system_clock::now();
  classifier->Train();
  auto duration = std::chrono::duration_cast< TimeT>
    (std::chrono::system_clock::now() - start);
  auto elapsed = duration.count();
  std::cout << "Training took " << elapsed << " ms\n";
  classifier->Save(modelfname);
}

int otbSharkImageClassificationFilter(int argc, char * argv[])
{
  if(argc<4 || argc>5)
    {
    std::cout << "Usage: input_image output_imae batchmode [in_model_name]\n";
    }
  std::string imfname = argv[1];
  std::string outfname = argv[2];
  bool batch = (std::string(argv[3])=="1");
  std::string modelfname = "/tmp/rf_model.txt";

  ReaderType::Pointer reader = ReaderType::New();
  reader->SetFileName(imfname);
  reader->UpdateOutputInformation();

  auto num_features = reader->GetOutput()->GetNumberOfComponentsPerPixel();

  std::cout << "Image has " << num_features << " bands\n";

  if(argc==5)
    {
    modelfname = argv[4];
    }
  else
    {
    buildModel(3, 1000, num_features, modelfname);
    }

  ClassificationFilterType::Pointer filter = ClassificationFilterType::New();

  MachineLearningModelType::Pointer model = MachineLearningModelType::New();
  model->Load(modelfname);
  filter->SetModel(model);
  filter->SetInput(reader->GetOutput());

  WriterType::Pointer writer = WriterType::New();
  writer->SetInput(filter->GetOutput());
  writer->SetFileName(outfname);
  std::cout << "Classification\n";
  filter->SetBatch(batch);
  using TimeT = std::chrono::milliseconds;
  auto start = std::chrono::system_clock::now();
  writer->Update();
  auto duration = std::chrono::duration_cast< TimeT>
    (std::chrono::system_clock::now() - start);
  auto elapsed = duration.count();
  std::cout << "Classification took " << elapsed << " ms\n";

  return EXIT_SUCCESS;
}
<|endoftext|>