Esempio n. 1
1
disparity::disparity ( const snark::imaging::camera_parser& left, const snark::imaging::camera_parser& right, unsigned int width, unsigned int height, const comma::csv::options& csv, bool input_rectified ):
    m_rotation( right.rotation() * left.rotation().transpose() ),
    m_translation( right.translation() - left.translation() ),
    m_rectify( left.camera(), left.distortion(), right.camera(), right.distortion(), width, height, m_rotation, m_translation, input_rectified ),
    m_input_rectified( input_rectified ),
    m_serialization( csv.fields, csv.format() )
{

}
Esempio n. 2
0
 int read_()
 {
     while( istream_.ready() || std::cin.good() )
     {
         const grid::input* p = istream_.read();
         if( !p || ( block_ && *block_ != p->block ) ) { handle_block_(); if( !p ) { break; } }
         block_ = p->block;
         voxel_map_t_::index_type i = {{ p->index.x(), p->index.y() }};
         std::pair< voxel_map_t_::iterator, bool > r = voxel_map_.base_type::insert( std::make_pair( i, voxel_() ) );
         if( !r.second )
         {
             if( permissive_ ) { continue; }
             std::cerr << "points-mesh: grid: got duplicated index: " << p->index.x() << "," << p->index.y() << "; if it is intended, use --permissive" << std::endl;
             return 1;
         }
         if( istream_.is_binary() )
         {
             r.first->second.resize( csv_.format().size() );
             std::memcpy( &r.first->second[0], istream_.binary().last(), csv_.format().size() );
         }
         else
         {
             r.first->second = comma::join( istream_.ascii().last(), csv_.delimiter );
         }
     }
     return 0;
 }
static block_t* read_block_impl_( ::tbb::flow_control* flow = NULL )
{
    static boost::array< block_t, 3 > blocks;
    static boost::optional< block_t::pair_t > last;
    static comma::uint32 block_id = 0;
    block_t::pairs_t* points = new block_t::pairs_t;
    while( true ) // quick and dirty, only if --discard
    {
        static comma::csv::input_stream< input_t > istream( std::cin, csv );
        points->clear();
        while( true )
        {
            if( last )
            {
                block_id = last->first.block;
                points->push_back( *last );
                last.reset();
            }
            if( is_shutdown || std::cout.bad() || std::cin.bad() || std::cin.eof() )
            {
                if( bursty_reader ) { bursty_reader->stop(); } // quick and dirty, it sucks...
                if( flow ) { flow->stop(); }
                break;
            }
            const input_t* p = istream.read();
            if( !p ) { break; }
            std::string line;
            if( csv.binary() )
            {
                line.resize( csv.format().size() );
                ::memcpy( &line[0], istream.binary().last(), csv.format().size() );
            }
            else
            {
                line = comma::join( istream.ascii().last(), csv.delimiter );
            }
            last = std::make_pair( *p, line );
            if( p->block != block_id ) { break; }
        }
        for( unsigned int i = 0; i < blocks.size(); ++i )
        {
            if( !blocks[i].empty ) { continue; }
            blocks[i].clear();
            blocks[i].id = block_id;
            blocks[i].points.reset( points );
            blocks[i].empty = false;
            return &blocks[i];
        }
    }
}
Esempio n. 4
0
 void output_( const iterator_& c1, const iterator_& b, const iterator_& c3 )
 {
     const iterator_& a = reverse_ ? c3 : c1;
     const iterator_& c = reverse_ ? c1 : c3;
     if( csv_.binary() )
     {
         std::cout.write( &a->second[0], csv_.format().size() );
         std::cout.write( &b->second[0], csv_.format().size() );
         std::cout.write( &c->second[0], csv_.format().size() );
     }
     else
     {
         std::cout << a->second << csv_.delimiter << b->second << csv_.delimiter << c->second << std::endl;
     }
 }
Esempio n. 5
0
inline static void init( comma::csv::options& csv_options, const comma::command_line_options& options, const std::string& defaultFields )
{
    csv_options.full_xpath = options.exists( "--full-xpath" );
    csv_options.fields = options.value( "--fields", defaultFields );
    if( options.exists( "--binary" ) )
    {
        boost::optional< std::string > format = options.optional< std::string >( "--binary" );
        if( format )
        {
            csv_options.format( options.value< std::string >( "--binary" ) );
        }
    }
    csv_options.precision = options.value< unsigned int >( "--precision", 12 );
    csv_options.delimiter = options.exists( "--delimiter" ) ? options.value( "--delimiter", ',' ) : options.value( "-d", ',' );
    boost::optional< std::string > quote_character = options.optional< std::string >( "--quote" );
    if( quote_character )
    {
        switch( quote_character->size() )
        {
            case 0: csv_options.quote.reset(); break;
            case 1: csv_options.quote = ( *quote_character )[0]; break;
            case 2: COMMA_THROW( comma::exception, "expected a quote character, got \"" << *quote_character << "\"" );
        }
    }
    csv_options.flush = options.exists( "--flush" );
}
Esempio n. 6
0
 intervals( const comma::command_line_options& options ) : options( options )
                                                         , csv( options )
                                                         , ocsv( options )
                                                         , ascii_csv( options )
                                                         , empty( traits< bound_type >::cast( options.optional< std::string >( "--empty" ) ) )
                                                         , intervals_only( options.exists( "--intervals-only" ) )
                                                         , use_limits( options.exists( "--limits,-l" ) )
 {
     if( csv.fields.empty() ) { csv.fields = comma::join( comma::csv::names< interval_t< From, To > >(), ',' ); }
     if( ocsv.fields.empty() || intervals_only )
     {
         ocsv.fields = comma::join( comma::csv::names< interval_t< From, To > >(), ',' );
         if( ocsv.binary() && ( intervals_only || append ) ) { ocsv.format( comma::csv::format::value< interval_t< From, To > >() ); }
     }
     ascii_csv.fields = ocsv.fields;
     ascii_csv.quote = boost::none;
     if( verbose ) { std::cerr << app_name << ": empty: "; empty ? std::cerr << *empty : std::cerr << "<none>"; std::cerr << std::endl; }
     options.assert_mutually_exclusive( "overlap-count-min,overlap-count-max", "overlap-count" );
     if( options.exists( "--overlap-count" ) )
     {
         min_overlap_count = max_overlap_count = options.value< unsigned int >( "--overlap-count" );
     }
     else
     {
         min_overlap_count = options.value( "--overlap-count-min", 0 );
         max_overlap_count = options.value( "--overlap-count-max", std::numeric_limits< unsigned int >::max() );
     }
 }
Esempio n. 7
0
template < typename It > static void output_( It it, It end )
{
    for( ; it != end; ++it )
    {
        for( std::size_t i = 0; i < it->second.size() ; ++i )
        {
            std::cout.write( &( it->second[i][0] ), stdin_csv.binary() ? stdin_csv.format().size() : it->second[i].length() );
            if( stdin_csv.flush ) { std::cout.flush(); }
        }
    }
}
Esempio n. 8
0
disparity::disparity ( const camera_parser& left, const camera_parser& right,
                 const cv::Mat& left_x, const cv::Mat& left_y, const cv::Mat& right_x, const cv::Mat& right_y,
                 const comma::csv::options& csv, bool input_rectified ):
    m_rotation( Eigen::Matrix3d::Identity() ),
    m_translation( right.translation() - left.translation() ),
    m_rectify( left.camera(), right.camera(), m_translation, left_x, left_y, right_x, right_y, input_rectified ),
    m_input_rectified( input_rectified ),
    m_serialization( csv.fields, csv.format() )
{
    
}
Esempio n. 9
0
stereo::stereo ( const snark::imaging::camera_parser& left, const snark::imaging::camera_parser& right, unsigned int width, unsigned int height, const comma::csv::options& csv, bool input_rectified ):
    m_rotation( right.rotation() * left.rotation().transpose() ),
    m_translation( right.translation() - left.translation() ),
    m_rectify( left.camera(), left.distortion(), right.camera(), right.distortion(), width, height, m_rotation, m_translation, input_rectified ),
    m_input_rectified( input_rectified ),
    m_frame_counter( 0 )
{
    if( csv.binary() )
    {
        m_binary.reset( new comma::csv::binary< colored_point >( csv ) );
        m_output.resize( csv.format().size() );
    }
    else
    {
        m_ascii.reset( new comma::csv::ascii< colored_point >( csv ) );
    }
}
Esempio n. 10
0
 intervals( const comma::command_line_options& options ) : options( options )
                                                         , csv( options )
                                                         , ocsv( options )
                                                         , ascii_csv( options )
                                                         , empty( traits< bound_type >::cast( options.optional< std::string >( "--empty" ) ) )
                                                         , intervals_only( options.exists( "--intervals-only" ) )
                                                         , use_limits( options.exists( "--limits,-l" ) )
 {
     if( csv.fields.empty() ) { csv.fields = comma::join( comma::csv::names< interval_t< From, To > >(), ',' ); }
     if( ocsv.fields.empty() || intervals_only )
     {
         ocsv.fields = comma::join( comma::csv::names< interval_t< From, To > >(), ',' );
         if( ocsv.binary() && intervals_only ) { ocsv.format( comma::csv::format::value< interval_t< From, To > >() ); }
     }
     ascii_csv.fields = ocsv.fields;
     ascii_csv.quote = boost::none;
     if( verbose ) { std::cerr << app_name << ": empty: "; empty ? std::cerr << *empty : std::cerr << "<none>"; std::cerr << std::endl; }
 }
Esempio n. 11
0
stereo::stereo ( const camera_parser& left, const camera_parser& right,
                 const cv::Mat& left_x, const cv::Mat& left_y, const cv::Mat& right_x, const cv::Mat& right_y,
                 const comma::csv::options& csv, bool input_rectified ):
    m_rotation( Eigen::Matrix3d::Identity() ),
    m_translation( right.translation() - left.translation() ),
    m_rectify( left.camera(), right.camera(), m_translation, left_x, left_y, right_x, right_y, input_rectified ),
    m_input_rectified( input_rectified ),
    m_frame_counter( 0 )
{
    if( csv.binary() )
    {
        m_binary.reset( new comma::csv::binary< colored_point >( csv ) );
        m_output.resize( csv.format().size() );
    }
    else
    {
        m_ascii.reset( new comma::csv::ascii< colored_point >( csv ) );
    }
}
Esempio n. 12
0
int sort( const comma::command_line_options& options )
{
    input_t::map sorted_map;
    input_t default_input;
    std::vector< std::string > v = comma::split( stdin_csv.fields, ',' );
    std::vector< std::string > order = options.exists("--order") ? comma::split( options.value< std::string >( "--order" ), ',' ) : v;
    std::vector< std::string > w (v.size());
    bool unique = options.exists("--unique,-u");
    
    std::string first_line;
    comma::csv::format f;
    if( stdin_csv.binary() ) { f = stdin_csv.format(); }
    else if( options.exists( "--format" ) ) { f = comma::csv::format( options.value< std::string >( "--format" ) ); }
    else
    {
        while( std::cin.good() && first_line.empty() ) { std::getline( std::cin, first_line ); }
        if( first_line.empty() ) { return 0; }
        f = comma::csv::impl::unstructured::guess_format( first_line, stdin_csv.delimiter );
        if( verbose ) { std::cerr << "csv-sort: guessed format: " << f.string() << std::endl; }
    }
    for( std::size_t i = 0; i < order.size(); ++i ) // quick and dirty, wasteful, but who cares
    {
        if (order[i].empty()) continue;
        for( std::size_t k = 0; k < v.size(); ++k )
        {
            if( v[k].empty() || v[k] != order[i] ) 
            { 
                if ( k + 1 == v.size()) 
                { 
                    std::cerr << "csv-sort: order field name \"" << order[i] << "\" not found in input fields \"" << stdin_csv.fields << "\"" << std::endl;
                    return 1;
                }
                continue; 
            }
            std::string type = default_input.keys.append( f.offset( k ).type );
            w[k] = "keys/" + type;
            
            ordering_t o;
            if ( type[0] == 's' ) { o.type = ordering_t::str_type; o.index = default_input.keys.strings.size() - 1; }
            else if ( type[0] == 'l' ) { o.type = ordering_t::long_type; o.index = default_input.keys.longs.size() - 1; }
            else if ( type[0] == 'd' ) { o.type = ordering_t::double_type; o.index = default_input.keys.doubles.size() - 1; }
            else if ( type[0] == 't' ) { o.type = ordering_t::time_type; o.index = default_input.keys.time.size() - 1; }
            else { std::cerr << "csv-sort: cannot sort on field " << v[k] << " of type \"" << type << "\"" << std::endl; return 1; }
            
            ordering.push_back(o);
            
            break;
        }
    }
    stdin_csv.fields = comma::join( w, ',' );
    if ( verbose ) { std::cerr << "csv-sort: fields: " << stdin_csv.fields << std::endl; }
    comma::csv::input_stream< input_t > stdin_stream( std::cin, stdin_csv, default_input );
    #ifdef WIN32
    if( stdin_stream.is_binary() ) { _setmode( _fileno( stdout ), _O_BINARY ); }
    #endif
    
    if (!first_line.empty()) 
    { 
        input_t::map::mapped_type& d = sorted_map[ comma::csv::ascii< input_t >(stdin_csv,default_input).get(first_line) ];
        d.push_back( first_line + "\n" );
    }
    
    while( stdin_stream.ready() || ( std::cin.good() && !std::cin.eof() ) )
    {
        const input_t* p = stdin_stream.read();
        if( !p ) { break; }
        if( stdin_stream.is_binary() )
        {
            input_t::map::mapped_type& d = sorted_map[ *p ];
            if (unique && !d.empty()) continue;
            d.push_back( std::string() );
            d.back().resize( stdin_csv.format().size() );
            ::memcpy( &d.back()[0], stdin_stream.binary().last(), stdin_csv.format().size() );
        }
        else
        {
            input_t::map::mapped_type& d = sorted_map[ *p ];
            if (unique && !d.empty()) continue;
            d.push_back( comma::join( stdin_stream.ascii().last(), stdin_csv.delimiter ) + "\n" );
        }
    }
    
    if( options.exists( "--reverse,-r" ) ) { output_( sorted_map.rbegin(), sorted_map.rend() ); }
    else { output_( sorted_map.begin(), sorted_map.end() ); }
    
    return 0;
}
Esempio n. 13
0
int main( int ac, char** av )
{
    try
    {
        comma::command_line_options options( ac, av );
        if( options.exists( "--help,-h" ) ) { usage(); }
        verbose = options.exists( "--verbose,-v" );
        bool is_or = options.exists( "--or" );
        csv = comma::csv::options( options );
        fields = comma::split( csv.fields, ',' );
        if( fields.size() == 1 && fields[0].empty() ) { fields.clear(); }
        std::vector< std::string > unnamed = options.unnamed( "--first-matching,--or,--sorted,--input-sorted,--not-matching,--output-all,--all,--strict,--verbose,-v", "-.*" );
        //for( unsigned int i = 0; i < unnamed.size(); constraints_map.insert( std::make_pair( comma::split( unnamed[i], ';' )[0], unnamed[i] ) ), ++i );
        bool strict = options.exists( "--strict" );
        bool first_matching = options.exists( "--first-matching" );
        bool not_matching = options.exists( "--not-matching" );
        bool all = options.exists( "--output-all,--all" );
        for( unsigned int i = 0; i < unnamed.size(); ++i )
        {
            std::string field = comma::split( unnamed[i], ';' )[0];
            bool found = false;
            for( unsigned int j = 0; j < fields.size() && !found; found = field == fields[j], ++j );
            if( !found )
            {
                if( strict ) { std::cerr << "csv-select: on constraint: \"" << unnamed[i] << "\" field \"" << field << "\" not found in fields: " << csv.fields << std::endl; return 1; }
                std::cerr << "csv-select: warning: on constraint: \"" << unnamed[i] << "\" field \"" << field << "\" not found in fields: " << csv.fields << std::endl;
                continue;
            }
            constraints_map.insert( std::make_pair( field, unnamed[i] ) );
        }
        comma::signal_flag is_shutdown;
        if( csv.binary() )
        {
            #ifdef WIN32
            _setmode( _fileno( stdout ), _O_BINARY );
            #endif
            init_input( csv.format(), options );
            comma::csv::binary_input_stream< input_t > istream( std::cin, csv, input );
            while( !is_shutdown && ( istream.ready() || ( std::cin.good() && !std::cin.eof() ) ) )
            {
                const input_t* p = istream.read();
                if( !p || p->done( is_or ) ) { break; }
                char match = ( p->is_a_match( is_or ) == !not_matching ) ? 1 : 0;
                if( match || all )
                {
                    std::cout.write( istream.last(), csv.format().size() );
                    if( all ) { std::cout.write( &match, 1 ); }
                    if( csv.flush ) { std::cout.flush(); }
                    if( first_matching ) { break; }
                }
            }
        }
        else
        {
            std::string line;
            while( !is_shutdown && std::cin.good() && !std::cin.eof() )
            {
                std::getline( std::cin, line );
                line = comma::strip( line, '\r' ); // windows, sigh...
                if( !line.empty() ) { break; }
            }
            if( line.empty() ) { return 0; }
            comma::csv::format format = options.exists( "--format" )
                                      ? comma::csv::format( options.value< std::string >( "--format" ) )
                                      : guess_format( line );
            init_input( format, options );
            comma::csv::ascii_input_stream< input_t > istream( std::cin, csv, input );
            // todo: quick and dirty: no time to debug why the commented section does not work (but that's the right way)
            std::istringstream iss( line );
            comma::csv::ascii_input_stream< input_t > isstream( iss, csv, input );
            const input_t* p = isstream.read();
            if( !p || p->done( is_or ) ) { return 0; }
            bool match = p->is_a_match( is_or ) == !not_matching;
            if( match || all )
            {
                std::cout << line;
                if( all ) { std::cout << csv.delimiter << match; }
                std::cout << std::endl;
                if( first_matching ) { return 0; }
            }
            while( !is_shutdown && ( istream.ready() || ( std::cin.good() && !std::cin.eof() ) ) )
            {
                const input_t* p = istream.read();
                if( !p || p->done( is_or ) ) { break; }
                bool match = p->is_a_match( is_or ) == !not_matching;
                if( match || all )
                {
                    std::cout << comma::join( istream.last(), csv.delimiter );
                    if( all ) { std::cout << csv.delimiter << match; }
                    std::cout << std::endl;
                    if( first_matching ) { return 0; }
                }
            }
        }
        return 0;
    }
    catch( std::exception& ex ) { std::cerr << "csv-select: caught: " << ex.what() << std::endl; }
    catch( ... ) { std::cerr << "csv-select: unknown exception" << std::endl; }
    return 1;
}
Esempio n. 14
0
int main( int ac, char** av )
{
    comma::command_line_options options( ac, av );
    verbose = options.exists( "--verbose,-v" );
    if( options.exists( "--help,-h" ) ) { usage(verbose); }

    try
    {
        options.assert_mutually_exclusive("--tcp,--serial");
        options.assert_mutually_exclusive("--laser,--serial");
        scan_break = options.value< comma::uint32 > ( "--scan-break", 20 ); // time in us
        num_of_scans = options.value< comma::uint32 > ( "--num-of-scans", 100 ); // time in us
        reboot_on_error = options.exists( "--reboot-on-error" );
        start_step=options.value<int>("--start-step", -1);
        end_step=options.value<int>("--end-step", -1);
        debug_verbose=options.exists("--debug");
        frames=options.value<int>("--frames",-1);
        omit_on_error = ! options.exists("--dont-omit-on-error");
        bool permissive = options.exists( "--permissive" );
        std::vector< std::string > unnamed = options.unnamed( "--output-fields,--output-format,--format,--verbose,-v,--flush,--reboot-on-error,--debug,--output-samples,--dont-omit-on-errork,--permissive",
                                                              "--frames,--scan-break,--num-of-scans,--start-step,--end-step,--serial,--port,--tcp,--laser,--fields,--binary,-b,--baud-rate,--set-baud-rate");
        if(!unnamed.empty()) { std::cerr<<"invalid option(s):"<< comma::join(unnamed, ',') <<std::endl; return 1; }
        
        scip2_device scip2;
        ust_device<UST_SMALL_STEPS> ust_small( permissive );
        ust_device<UST_MAX_STEPS> ust_max( permissive );
        const bool serial = options.exists( "--serial");
        laser_device& device = serial ? scip2 : (start_step ?  (laser_device&)ust_small : (laser_device&)ust_max);
        
        // Sets up output data
        csv.fields = options.value< std::string >( "--fields", "" );
        std::vector< std::string > v = comma::split( csv.fields, ',' );
        for( std::size_t i = 0; i < v.size(); ++i ) // convenience shortcuts
        {
            if( v[i] == "i" ) { v[i] = "intensity"; }
            else if( v[i] == "r" ) { v[i] = "range"; }
            else if( v[i] == "b" ) { v[i] = "bearing"; }
            else if( v[i] == "e" ) { v[i] = "elevation"; }
        }
        csv.fields = comma::join( v, ',' );
        csv.full_xpath = false;
        // see sick-ldmrs-to-csv
        if( options.exists( "--output-format,--format" ) ) 
        {
            if( !options.exists("--tcp") && !options.exists("--serial") ) { std::cerr << name() << "please specify --serial or --tcp" << std::endl; return 1; }
            if(serial) { std::cout << comma::csv::format::value< scip2_device::output_t >( csv.fields, false ) << std::endl; }
            else { std::cout << comma::csv::format::value< snark::hokuyo::data_point >( csv.fields, false ) << std::endl; }
            return 0; 
            
        }
        if(options.exists("--output-fields"))
        {
            if( !options.exists("--tcp") && !options.exists("--serial") ) { std::cerr << name() << "please specify --serial or --tcp" << std::endl; return 1; }
            if(!csv.fields.empty()) {std::cout<<csv.fields<<std::endl;}
            else if(serial) { std::cout<<comma::join(comma::csv::names<scip2_device::output_t>(), ',')<<std::endl;}
            else {std::cout<<comma::join(comma::csv::names<snark::hokuyo::data_point>(), ',')<<std::endl;}
            return 0;
        }
        if( options.exists( "--binary,-b" ) ) 
        {
            if(serial) { csv.format( comma::csv::format::value< scip2_device::output_t >( csv.fields, false ) ); }
            else { csv.format( comma::csv::format::value< snark::hokuyo::data_point >( csv.fields, false ) ); }
        }
        
        if( options.exists( "--output-samples" ) ) { output_samples(); return 0; }
        
        device.init(options);
        /// Connect to the laser
        ios = device.connect();
        device.setup(*ios);
        if(serial)
        {
            if(debug_verbose) { ((serial_stream&)*ios).dump_setting(); }
//             debug_msg("change serial settings");
//             const char* ss_cmd="SS500000\n";
            process(scip2);
        }
        else
        {
            if(start_step) { process(ust_small); } else { process(ust_max); }
        }
    }
    catch( std::exception& ex ) { std::cerr << name() << ex.what() << std::endl; return 1; }
    catch( ... ) { std::cerr << name() << "unknown exception" << std::endl; return 1; }
    return 0;
}
Esempio n. 15
0
int main( int ac, char** av )
{
    try
    {
        comma::command_line_options options( ac, av );
        verbose = options.exists( "--verbose,-v" );
        if( options.exists( "--help,-h" ) ) { usage( verbose ); }
        csv = comma::csv::options( options );
        csv.full_xpath = true;
        ascii = comma::csv::ascii< Eigen::Vector3d >( "x,y,z", csv.delimiter );
        const std::vector< std::string >& operations = options.unnamed( "--verbose,-v,--trace,--no-antialiasing,--next,--unit", "-.*" );
        if( operations.size() != 1 ) { std::cerr << "points-calc: expected one operation, got " << operations.size() << ": " << comma::join( operations, ' ' ) << std::endl; return 1; }
        const std::string& operation = operations[0];
        if (vector_calc::has_operation(operation))
        {
            vector_calc::process(operation, options, csv);
            return 0;
        }
        if( operation == "plane-intersection" )
        {
            plane_intersection::process(options, csv);
            return 0;
        }
        if( operation == "distance" )
        {
            if(    csv.has_field( "first" )   || csv.has_field( "second" )
                || csv.has_field( "first/x" ) || csv.has_field( "second/x" )
                || csv.has_field( "first/y" ) || csv.has_field( "second/y" )
                || csv.has_field( "first/z" ) || csv.has_field( "second/z" ) )
            {
                calculate_distance_for_pairs();
                return 0;
            }
            if ( options.exists( "--next" ) ) { calculate_distance_next(); }
            else { calculate_distance( false ); }
            return 0;
        }
        if( operation == "cumulative-distance" )
        {
            calculate_distance( true );
            return 0;
        }
        if( operation == "nearest" )
        {
            if( options.exists( "--point,--to" ) )
            {
                Eigen::Vector3d point = comma::csv::ascii< Eigen::Vector3d >().get( options.value< std::string >( "--point,--to" ) );
                comma::csv::input_stream< Eigen::Vector3d > istream( std::cin, csv );
                std::string record;
                double min_distance = std::numeric_limits< double >::max();
                while( istream.ready() || ( std::cin.good() && !std::cin.eof() ) )
                {
                    const Eigen::Vector3d* p = istream.read();
                    if( !p ) { break; }
                    double d = ( *p - point ).norm();
                    if( d >= min_distance ) { continue; }
                    min_distance = d;
                    record = csv.binary() ? std::string( istream.binary().last(), csv.format().size() ) : comma::join( istream.ascii().last(), csv.delimiter );
                }
                if( !record.empty() ) { std::cout << record; }
                if( csv.binary() ) { std::cout.write( reinterpret_cast< const char* >( &min_distance ), sizeof( double ) ); }
                else { std::cout << csv.delimiter << min_distance << std::endl; }
                return 0;
            }
            else
            {
                
                return 0;
            }
        }
        if( operation == "thin" )
        {
            if( !options.exists( "--resolution" ) ) { std::cerr << "points-calc: --resolution is not specified " << std::endl; return 1; }
            double resolution = options.value( "--resolution" , 0.0 );
            thin( resolution );
            return 0;
        }
        if( operation == "discretise" || operation == "discretize" )
        {
            if( !options.exists( "--step" ) ) { std::cerr << "points-calc: --step is not specified " << std::endl; return 1; }
            double step = options.value( "--step" , 0.0 );
            if( step <= 0 ) { std::cerr << "points-calc: expected positive step, got " << step << std::endl; return 1; }
            // the last discretised point can be very close to the end of the interval, in which case the last two points can be identical in the output since ascii.put uses 12 digits by default
            // setting --tolerance=1e-12 will not allow the last discretised point to be too close to the end of the interval and therefore the output will have two distinct points at the end
            double tolerance = options.value( "--tolerance" , 0.0 ); 
            if( tolerance < 0 ) { std::cerr << "points-calc: expected non-negative tolerance, got " << tolerance << std::endl; return 1; }
            discretise( step, tolerance );
            return 0;
        }
        if( operation == "local-max" || operation == "local-min" ) // todo: if( operation == "local-calc" ? )
        {
            double sign = operation == "local-max" ? 1 : -1;
            if( csv.fields.empty() ) { csv.fields = "x,y,z,scalar"; }
            csv.full_xpath = false;
            bool has_id = csv.has_field( "id" );
            comma::csv::input_stream< local_operation::point > istream( std::cin, csv );
            std::deque< local_operation::record > records;
            double radius = options.value< double >( "--radius" );
            bool trace = options.exists( "--trace" );
            Eigen::Vector3d resolution( radius, radius, radius );
            snark::math::closed_interval< double, 3 > extents;
            comma::uint32 id = 0;
            if( verbose ) { std::cerr << "points-calc: reading input points..." << std::endl; }
            while( istream.ready() || ( std::cin.good() && !std::cin.eof() ) )
            {
                const local_operation::point* p = istream.read();
                if( !p ) { break; }
                std::string line;
                if( csv.binary() ) // quick and dirty
                {
                    line.resize( csv.format().size() );
                    ::memcpy( &line[0], istream.binary().last(), csv.format().size() );
                }
                else
                {
                    line = comma::join( istream.ascii().last(), csv.delimiter );
                }
                local_operation::point q = *p;
                if( !has_id ) { q.id = id++; }
                records.push_back( local_operation::record( q, line ) );
                records.back().reference_record = &records.back();
                extents.set_hull( p->coordinates );
            }
            if( verbose ) { std::cerr << "points-calc: loading " << records.size() << " points into grid..." << std::endl; }
            typedef std::vector< local_operation::record* > voxel_t; // todo: is vector a good container? use deque
            typedef snark::voxel_map< voxel_t, 3 > grid_t;
            grid_t grid( extents.min(), resolution );
            for( std::size_t i = 0; i < records.size(); ++i ) { ( grid.touch_at( records[i].point.coordinates ) )->second.push_back( &records[i] ); }
            if( verbose ) { std::cerr << "points-calc: searching for local extrema..." << std::endl; }
            for( grid_t::iterator it = grid.begin(); it != grid.end(); ++it )
            {
                grid_t::index_type i;
                for( i[0] = it->first[0] - 1; i[0] < it->first[0] + 2; ++i[0] )
                {
                    for( i[1] = it->first[1] - 1; i[1] < it->first[1] + 2; ++i[1] )
                    {
                        for( i[2] = it->first[2] - 1; i[2] < it->first[2] + 2; ++i[2] )
                        {
                            grid_t::iterator git = grid.find( i );
                            if( git == grid.end() ) { continue; }
                            for( voxel_t::iterator vit = it->second.begin(); vit != it->second.end(); ++vit )
                            {
                                for( std::size_t k = 0; k < git->second.size() && ( *vit )->is_extremum; ++k )
                                {
                                    local_operation::evaluate_local_extremum( *vit, git->second[k], radius, sign );
                                }
                            }
                        }
                    }
                }
            }
            #ifdef WIN32
            _setmode( _fileno( stdout ), _O_BINARY );
            #endif
            if( verbose ) { std::cerr << "points-calc: filling extrema grid..." << std::endl; }
            grid_t extrema( extents.min(), resolution );
            for( std::size_t i = 0; i < records.size(); ++i )
            {
                if( records[i].is_extremum )
                { 
                    ( extrema.touch_at( records[i].point.coordinates ) )->second.push_back( &records[i] );
                }
                else
                { 
                    records[i].extremum_id = local_operation::record::invalid_id; // quick and dirty for now
//                     if( records[i].extremum_id == local_operation::record::invalid_id ) { continue; }
//                     while( records[i].reference_record->point.id != records[i].reference_record->reference_record->point.id )
//                     {
//                         records[i].reference_record = records[i].reference_record->reference_record;
//                     }
//                     records[i].extremum_id = records[i].reference_record->point.id;
                }
            }
            if( verbose ) { std::cerr << "points-calc: calculating distances to " << extrema.size() << " local extrema..." << std::endl; }
            for( grid_t::iterator it = grid.begin(); it != grid.end(); ++it )
            {
                grid_t::index_type i;
                for( i[0] = it->first[0] - 1; i[0] < it->first[0] + 2; ++i[0] )
                {
                    for( i[1] = it->first[1] - 1; i[1] < it->first[1] + 2; ++i[1] )
                    {
                        for( i[2] = it->first[2] - 1; i[2] < it->first[2] + 2; ++i[2] )
                        {
                            grid_t::iterator git = extrema.find( i );
                            if( git == extrema.end() ) { continue; }
                            for( std::size_t n = 0; n < it->second.size(); ++n )
                            {                            
                                for( std::size_t k = 0; k < git->second.size(); ++k )
                                {
                                    local_operation::update_nearest_extremum( it->second[n], git->second[k], radius );
                                }
                            }
                        }
                    }
                }
            }
            if( trace )
            {
                if( verbose ) { std::cerr << "points-calc: tracing extrema..." << std::endl; }
                for( std::size_t i = 0; i < records.size(); ++i )
                {
                    if( records[i].extremum_id == local_operation::record::invalid_id ) { continue; }
                    while( records[i].reference_record->point.id != records[i].reference_record->reference_record->point.id )
                    {
                        records[i].reference_record = records[i].reference_record->reference_record;
                    }
                }
            }
            if( verbose ) { std::cerr << "points-calc: outputting..." << std::endl; }
            std::string endl = csv.binary() ? "" : "\n";
            std::string delimiter = csv.binary() ? "" : std::string( 1, csv.delimiter );
            comma::csv::options output_csv;
            if( csv.binary() ) { output_csv.format( "ui,d" ); }
            comma::csv::output_stream< local_operation::output > ostream( std::cout, output_csv );
            for( std::size_t i = 0; i < records.size(); ++i )
            {
                std::cout.write( &records[i].line[0], records[i].line.size() );
                std::cout.write( &delimiter[0], delimiter.size() );
                ostream.write( records[i].output( false ) ); // quick and dirty
            }
            if( verbose ) { std::cerr << "points-calc: done!" << std::endl; }
            return 0;
        }
        if( operation == "nearest-max" || operation == "nearest-min" || operation == "nearest-any" )
        {
            double sign = operation == "nearest-max" ? 1 : -1;
            bool any = operation == "nearest-any";
            if( csv.fields.empty() ) { csv.fields = "x,y,z,scalar"; }
            csv.full_xpath = false;
            bool has_id = csv.has_field( "id" );
            comma::csv::input_stream< local_operation::point > istream( std::cin, csv );
            std::deque< local_operation::record > records;
            double radius = options.value< double >( "--radius" );
            Eigen::Vector3d resolution( radius, radius, radius );
            snark::math::closed_interval< double, 3 > extents;
            comma::uint32 id = 0;
            if( verbose ) { std::cerr << "points-calc: reading input points..." << std::endl; }
            while( istream.ready() || ( std::cin.good() && !std::cin.eof() ) )
            {
                const local_operation::point* p = istream.read();
                if( !p ) { break; }
                std::string line;
                if( csv.binary() ) // quick and dirty
                {
                    line.resize( csv.format().size() );
                    ::memcpy( &line[0], istream.binary().last(), csv.format().size() );
                }
                else
                {
                    line = comma::join( istream.ascii().last(), csv.delimiter );
                }
                local_operation::point q = *p;
                if( !has_id ) { q.id = id++; }
                records.push_back( local_operation::record( q, line ) );
                records.back().reference_record = &records.back();
                extents.set_hull( p->coordinates );
            }
            if( verbose ) { std::cerr << "points-calc: loading " << records.size() << " points into grid..." << std::endl; }
            typedef std::vector< local_operation::record* > voxel_t; // todo: is vector a good container? use deque
            typedef snark::voxel_map< voxel_t, 3 > grid_t;
            grid_t grid( extents.min(), resolution );
            for( std::size_t i = 0; i < records.size(); ++i ) { ( grid.touch_at( records[i].point.coordinates ) )->second.push_back( &records[i] ); }
            if( verbose ) { std::cerr << "points-calc: searching for " << operation << "..." << std::endl; }
            for( grid_t::iterator it = grid.begin(); it != grid.end(); ++it )
            {
                grid_t::index_type i;
                for( i[0] = it->first[0] - 1; i[0] < it->first[0] + 2; ++i[0] )
                {
                    for( i[1] = it->first[1] - 1; i[1] < it->first[1] + 2; ++i[1] )
                    {
                        for( i[2] = it->first[2] - 1; i[2] < it->first[2] + 2; ++i[2] )
                        {
                            grid_t::iterator git = grid.find( i );
                            if( git == grid.end() ) { continue; }
                            for( std::size_t n = 0; n < it->second.size(); ++n )
                            {                            
                                for( std::size_t k = 0; k < git->second.size(); ++k )
                                {
                                    local_operation::update_nearest( it->second[n], git->second[k], radius, sign, any );
                                }
                            }
                        }
                    }
                }
            }
            #ifdef WIN32
            _setmode( _fileno( stdout ), _O_BINARY );
            #endif
            if( verbose ) { std::cerr << "points-calc: outputting..." << std::endl; }
            std::string endl = csv.binary() ? "" : "\n";
            std::string delimiter = csv.binary() ? "" : std::string( 1, csv.delimiter );
            comma::csv::options output_csv;
            if( csv.binary() ) { output_csv.format( "ui,d" ); }
            comma::csv::output_stream< local_operation::output > ostream( std::cout, output_csv );
            for( std::size_t i = 0; i < records.size(); ++i )
            {
                std::cout.write( &records[i].line[0], records[i].line.size() );
                std::cout.write( &delimiter[0], delimiter.size() );
                ostream.write( records[i].output() );
            }
            if( verbose ) { std::cerr << "points-calc: done!" << std::endl; }
            return 0;
        }
        if( operation == "find-outliers" )
        {
            unsigned int size = options.value< unsigned int >( "--min-number-of-points-per-voxel,--size" );
            double r = options.value< double >( "--resolution" );
            Eigen::Vector3d resolution( r, r, r );
            bool no_antialiasing = options.exists( "--no-antialiasing" );            
            comma::csv::input_stream< Eigen::Vector3d > istream( std::cin, csv );
            std::deque< remove_outliers::record > records;
            snark::math::closed_interval< double, 3 > extents;
            if( verbose ) { std::cerr << "points-calc: reading input points..." << std::endl; }
            while( istream.ready() || ( std::cin.good() && !std::cin.eof() ) )
            {
                const Eigen::Vector3d* p = istream.read();
                if( !p ) { break; }
                std::string line;
                if( csv.binary() ) // quick and dirty
                {
                    line.resize( csv.format().size() );
                    ::memcpy( &line[0], istream.binary().last(), csv.format().size() );
                }
                else
                {
                    line = comma::join( istream.ascii().last(), csv.delimiter );
                }
                records.push_back( remove_outliers::record( *p, line ) );
                extents.set_hull( *p );
            }
            if( verbose ) { std::cerr << "points-calc: loading " << records.size() << " points into grid..." << std::endl; }
            typedef std::vector< remove_outliers::record* > voxel_t; // todo: is vector a good container? use deque
            typedef snark::voxel_map< voxel_t, 3 > grid_t;
            grid_t grid( extents.min(), resolution );
            for( std::size_t i = 0; i < records.size(); ++i ) { ( grid.touch_at( records[i].point ) )->second.push_back( &records[i] ); }
            if( verbose ) { std::cerr << "points-calc: removing outliers..." << std::endl; }
            for( grid_t::iterator it = grid.begin(); it != grid.end(); ++it )
            {
                bool rejected = true;
                if( no_antialiasing )
                {
                    rejected = it->second.size() < size;
                }
                else
                {
                    grid_t::index_type i;
                    for( i[0] = it->first[0] - 1; i[0] < it->first[0] + 2 && rejected; ++i[0] )
                    {
                        for( i[1] = it->first[1] - 1; i[1] < it->first[1] + 2 && rejected; ++i[1] )
                        {
                            for( i[2] = it->first[2] - 1; i[2] < it->first[2] + 2 && rejected; ++i[2] )
                            {
                                grid_t::iterator git = grid.find( i );
                                rejected = git == grid.end() || git->second.size() < size;
                            }
                        }
                    }
                }
                if( rejected ) { for( std::size_t i = 0; i < it->second.size(); ++i ) { it->second[i]->rejected = true; } }
            }
            #ifdef WIN32
            _setmode( _fileno( stdout ), _O_BINARY );
            #endif
            if( verbose ) { std::cerr << "points-calc: outputting..." << std::endl; }
            std::string endl = csv.binary() ? "" : "\n";
            std::string delimiter = csv.binary() ? "" : std::string( 1, csv.delimiter );
            for( std::size_t i = 0; i < records.size(); ++i )
            {
                char valid = records[i].rejected ? 0 : 1;
                std::cout.write( &records[i].line[0], records[i].line.size() );
                std::cout.write( &delimiter[0], delimiter.size() );
                std::cout.write( &valid, 1 );
                std::cout.write( &endl[0], endl.size() );
            }
            if( verbose ) { std::cerr << "points-calc: done!" << std::endl; }
            return 0;
        }
        std::cerr << "points-calc: please specify an operation" << std::endl;
        return 1;
    }
    catch( std::exception& ex )
    {
        std::cerr << "points-calc: " << ex.what() << std::endl;
    }
    catch( ... )
    {
        std::cerr << "points-calc: unknown exception" << std::endl;
    }
    return 1;
}
Esempio n. 16
0
static void output_input( std::ostream& os, const timestring_t& input )
{
    if( stdin_csv.binary() ) { os.write( &input.second[0], stdin_csv.format().size() ); }
    else { os << input.second; }
}
Esempio n. 17
0
int main( int argc, char** argv )
{
    ::setenv( "PYLON_ROOT", STRINGIZED( BASLER_PYLON_DIR ), 0 );
    ::setenv( "GENICAM_ROOT_V2_1", STRINGIZED( BASLER_PYLON_GENICAM_DIR ), 0 );
    try
    {
        unsigned int id;
        std::string address;
        std::string setAttributes;
        unsigned int discard;
        boost::program_options::options_description description( "options" );
        unsigned int packet_size;
        unsigned int exposure;
        unsigned int gain;
        unsigned int offset_x;
        unsigned int offset_y;
        unsigned int width;
        unsigned int height;
        std::string frame_trigger;
        std::string line_trigger;
        unsigned int line_rate;
        double timeout_seconds;
        description.add_options()
            ( "help,h", "display help message" )
            ( "address", boost::program_options::value< std::string >( &address ), "camera ip address; default: connect to the first available camera" )
            ( "discard", "discard frames, if cannot keep up; same as --buffer=1" )
            ( "buffer", boost::program_options::value< unsigned int >( &discard )->default_value( 0 ), "maximum buffer size before discarding frames, default: unlimited" )
            ( "list-cameras", "output camera list and exit" )
            ( "fields,f", boost::program_options::value< std::string >( &options.fields )->default_value( "t,rows,cols,type" ), "header fields, possible values: t,rows,cols,type,size,counters" )
            ( "image-type", boost::program_options::value< std::string >( &options.type )->default_value( "3ub" ), "image type, e.g. '3ub'; also see --long-help for details" )
            ( "offset-x", boost::program_options::value< unsigned int >( &offset_x )->default_value( 0 ), "offset in pixels in the line" )
            ( "offset-y", boost::program_options::value< unsigned int >( &offset_y )->default_value( 0 ), "offset in lines in the frame" )
            ( "width", boost::program_options::value< unsigned int >( &width )->default_value( std::numeric_limits< unsigned int >::max() ), "line width in pixels; default: max" )
            ( "height", boost::program_options::value< unsigned int >( &height )->default_value( std::numeric_limits< unsigned int >::max() ), "number of lines in frame (in chunk mode always 1); default: max" )
            ( "frame-trigger", boost::program_options::value< std::string >( &frame_trigger ), "'line1', 'line2', 'line3', 'encoder'" ) //; if absent while --line-trigger present, same as --line-trigger" )
            ( "line-trigger", boost::program_options::value< std::string >( &line_trigger ), "'line1', 'line2', 'line3', 'encoder'" )
            ( "line-rate", boost::program_options::value< unsigned int >( &line_rate ), "line aquisition rate" )
            ( "encoder-ticks", boost::program_options::value< unsigned int >( &encoder_ticks ), "number of encoder ticks until the counter resets (reused for line number in frame in chunk mode)" )
            ( "header-only", "output header only" )
            ( "no-header", "output image data only" )
            ( "packet-size", boost::program_options::value< unsigned int >( &packet_size ), "mtu size on camera side, should be not greater than your lan and network interface set to" )
            ( "exposure", boost::program_options::value< unsigned int >( &exposure )->default_value( 100 ), "exposure" )
            ( "gain", boost::program_options::value< unsigned int >( &gain )->default_value( 100 ), "gain" )
            ( "timeout", boost::program_options::value< double >( &timeout_seconds )->default_value( 3.0 ), " frame acquisition timeout" )
            ( "test-colour", "output colour test image" )
            ( "verbose,v", "be more verbose" );
        boost::program_options::variables_map vm;
        boost::program_options::store( boost::program_options::parse_command_line( argc, argv, description), vm );
        boost::program_options::parsed_options parsed = boost::program_options::command_line_parser(argc, argv).options( description ).allow_unregistered().run();
        boost::program_options::notify( vm );
        if ( vm.count( "help" ) || vm.count( "long-help" ) )
        {
            std::cerr << "acquire images from a basler camera (for now gige only)" << std::endl;
            std::cerr << "output to stdout as serialized cv::Mat" << std::endl;
            std::cerr << std::endl;
            std::cerr << "usage: basler-cat [<options>] [<filters>]" << std::endl;
            std::cerr << std::endl;
            std::cerr << description << std::endl;
            if( vm.count( "long-help" ) )
            {
                std::cerr << std::endl;
                std::cerr << snark::cv_mat::filters::usage() << std::endl;
                std::cerr << std::endl;
                std::cerr << snark::cv_mat::serialization::options::type_usage() << std::endl;
            }
            std::cerr << std::endl;
            std::cerr << "note: there is a glitch or a subtle feature in basler line camera:" << std::endl;
            std::cerr << "      - power-cycle camera" << std::endl;
            std::cerr << "      - view colour images: it works" << std::endl;
            std::cerr << "      - view grey-scale images: it works" << std::endl;
            std::cerr << "      - view colour images: it still displays grey-scale" << std::endl;
            std::cerr << "      even in their native viewer you need to set colour image" << std::endl;
            std::cerr << "      repeatedly and with pure luck it works, but we have not" << std::endl;
            std::cerr << "      managed to do it in software; the remedy: power-cycle the camera" << std::endl;
            std::cerr << std::endl;
            return 1;
        }
        verbose = vm.count( "verbose" );
        if( verbose )
        {
            std::cerr << "basler-cat: PYLON_ROOT=" << ::getenv( "PYLON_ROOT" ) << std::endl;
            std::cerr << "basler-cat: GENICAM_ROOT_V2_1=" << ::getenv( "GENICAM_ROOT_V2_1" ) << std::endl;
            std::cerr << "basler-cat: initializing camera..." << std::endl;
        }
        Pylon::PylonAutoInitTerm auto_init_term;
        Pylon::CTlFactory& factory = Pylon::CTlFactory::GetInstance();
        Pylon::ITransportLayer* transport_layer( Pylon::CTlFactory::GetInstance().CreateTl( Pylon::CBaslerGigECamera::DeviceClass() ) );        
        if( !transport_layer )
        { 
            std::cerr << "basler-cat: failed to create transport layer" << std::endl;
            std::cerr << "            most likely PYLON_ROOT and GENICAM_ROOT_V2_1 environment variables not set" << std::endl;
            std::cerr << "            point them to your pylon installation, e.g:" << std::endl;
            std::cerr << "            export PYLON_ROOT=/opt/pylon" << std::endl;
            std::cerr << "            export GENICAM_ROOT_V2_1=/opt/pylon/genicam" << std::endl;
            return 1;
        }
        if( vm.count( "list-cameras" ) )
        {
            Pylon::DeviceInfoList_t devices;
            factory.EnumerateDevices( devices );
            for( unsigned int i = 0; i < devices.size(); ++i ) { std::cerr << devices[i].GetFullName() << std::endl; }
            return 0;
        }
        timeout = timeout_seconds * 1000.0;
        std::string filters = comma::join( boost::program_options::collect_unrecognized( parsed.options, boost::program_options::include_positional ), ';' );
        options.header_only = vm.count( "header-only" );
        options.no_header = vm.count( "no-header" );
        csv = comma::csv::options( argc, argv );
        bool chunk_mode =    csv.has_field( "counters" ) // quick and dirty
                          || csv.has_field( "adjusted-t" )
                          || csv.has_field( "line" )
                          || csv.has_field( "line-count" )
                          || csv.has_field( "ticks" )
                          || csv.has_field( "counters/adjusted-t" )
                          || csv.has_field( "counters/line" )
                          || csv.has_field( "counters/line-count" )
                          || csv.has_field( "counters/ticks" );
        if( chunk_mode )
        {
            if( vm.count( "encoder-ticks" ) == 0 ) { std::cerr << "basler-cat: chunk mode, please specify --encoder-ticks" << std::endl; return 1; }
            if( !filters.empty() ) { std::cerr << "basler-cat: chunk mode, cannot handle filters; use: basler-cat | cv-cat <filters> instead" << std::endl; return 1; }
            if( height != 1 && height != std::numeric_limits< unsigned int >::max() ) { std::cerr << "basler-cat: only --height=1 implemented in chunk mode" << std::endl; return 1; }
            height = 1;
            std::vector< std::string > v = comma::split( csv.fields, ',' );
            std::string format;
            for( unsigned int i = 0; i < v.size(); ++i )
            {
                if( v[i] == "t" ) { v[i] = "header/" + v[i]; format += "t"; }
                else if( v[i] == "rows" || v[i] == "cols" || v[i] == "size" || v[i] == "type" ) { v[i] = "header/" + v[i]; format += "ui"; }
                else if( v[i] == "adjusted-t" ) { v[i] = "counters/" + v[i]; format += "t"; }
                else if( v[i] == "line-count" || v[i] == "ticks" ) { v[i] = "counters/" + v[i]; format += "ul"; }
                else if( v[i] == "line" ) { v[i] = "counters/" + v[i]; format += "ui"; }
                else { std::cerr << "basler-cat: expected field, got '" << v[i] << "'" << std::endl; return 1; }
            }
            csv.fields = comma::join( v, ',' );
            csv.full_xpath = true;
            csv.format( format );
        }
        if( !vm.count( "buffer" ) && vm.count( "discard" ) ) { discard = 1; }
        Pylon::CBaslerGigECamera camera;
        if( vm.count( "address" ) )
        {
            Pylon::CBaslerGigEDeviceInfo info;
            info.SetIpAddress( address.c_str() );
            camera.Attach( factory.CreateDevice( info ) );
        }
        else
        {
            Pylon::DeviceInfoList_t devices;
            factory.EnumerateDevices( devices );
            if( devices.empty() ) { std::cerr << "basler-cat: no camera found" << std::endl; return 1; }
            std::cerr << "basler-cat: will connect to the first of " << devices.size() << " found device(s):" << std::endl;
            for( unsigned int i = 0; i < devices.size(); ++i ) { std::cerr << "    " << devices[i].GetFullName() << std::endl; }
            camera.Attach( transport_layer->CreateDevice( devices[0] ) );
        }
        if( verbose ) { std::cerr << "basler-cat: initialized camera" << std::endl; }
        if( verbose ) { std::cerr << "basler-cat: opening camera " << camera.GetDevice()->GetDeviceInfo().GetFullName() << "..." << std::endl; }
        camera.Open();
        if( verbose ) { std::cerr << "basler-cat: opened camera " << camera.GetDevice()->GetDeviceInfo().GetFullName() << std::endl; }
        Pylon::CBaslerGigECamera::StreamGrabber_t grabber( camera.GetStreamGrabber( 0 ) );
        grabber.Open();
        unsigned int channels;
        switch( options.get_header().type ) // quick and dirty
        {
            case CV_8UC1:
                channels = set_pixel_format_( camera, Basler_GigECamera::PixelFormat_Mono8 );
                break;
            case CV_8UC3:
                channels = set_pixel_format_( camera, Basler_GigECamera::PixelFormat_RGB8Packed );
                break;
            default:
                std::cerr << "basler-cat: type \"" << options.type << "\" not implemented or not supported by camera" << std::endl;
                return 1;
        }
        unsigned int max_width = camera.Width.GetMax();
        if( offset_x >= max_width ) { std::cerr << "basler-cat: expected --offset-x less than " << max_width << ", got " << offset_x << std::endl; return 1; }
        camera.OffsetX.SetValue( offset_x );
        width = ( ( unsigned long long )( offset_x ) + width ) < max_width ? width : max_width - offset_x;
        camera.Width.SetValue( width );
        unsigned int max_height = camera.Height.GetMax();
        //if( height < 512 ) { std::cerr << "basler-cat: expected height greater than 512, got " << height << std::endl; return 1; }
        
        // todo: is the colour line 2098 * 3 or ( 2098 / 3 ) * 3 ?
        //offset_y *= channels;
        //height *= channels;
        
        if( offset_y >= max_height ) { std::cerr << "basler-cat: expected --offset-y less than " << max_height << ", got " << offset_y << std::endl; return 1; }
        camera.OffsetY.SetValue( offset_y );
        height = ( ( unsigned long long )( offset_y ) + height ) < max_height ? height : ( max_height - offset_y );
        camera.Height.SetValue( height );
        if( verbose ) { std::cerr << "basler-cat: set width,height to " << width << "," << height << std::endl; }
        if( vm.count( "packet-size" ) ) { camera.GevSCPSPacketSize.SetValue( packet_size ); }
        // todo: giving up... the commented code throws, but failure to stop acquisition, if active
        //       seems to lead to the following scenario:
        //       - power-cycle camera
        //       - view colour images: it works
        //       - view grey-scale images: it works
        //       - view colour images: it still displays grey-scale
        //if( verbose ) { std::cerr << "basler-cat: getting aquisition status... (frigging voodoo...)" << std::endl; }
        //GenApi::IEnumEntry* acquisition_status = camera.AcquisitionStatusSelector.GetEntry( Basler_GigECamera::AcquisitionStatusSelector_AcquisitionActive );
        //if( acquisition_status && GenApi::IsAvailable( acquisition_status ) && camera.AcquisitionStatus() )
        //{
        //    if( verbose ) { std::cerr << "basler-cat: stopping aquisition..." << std::endl; }
        //    camera.AcquisitionStop.Execute();
        //    if( verbose ) { std::cerr << "basler-cat: aquisition stopped" << std::endl; }
        //}
        
        // todo: a hack for now
        GenApi::IEnumEntry* acquisitionStart = camera.TriggerSelector.GetEntry( Basler_GigECamera::TriggerSelector_AcquisitionStart );
        if( acquisitionStart && GenApi::IsAvailable( acquisitionStart ) )
        {
            camera.TriggerSelector.SetValue( Basler_GigECamera::TriggerSelector_AcquisitionStart );
            camera.TriggerMode.SetValue( frame_trigger.empty() ? Basler_GigECamera::TriggerMode_Off : Basler_GigECamera::TriggerMode_On );
        }
        GenApi::IEnumEntry* frameStart = camera.TriggerSelector.GetEntry( Basler_GigECamera::TriggerSelector_FrameStart );
        if( frameStart && GenApi::IsAvailable( frameStart ) )
        {
            //if( frame_trigger.empty() ) { frame_trigger = line_trigger; }
            if( frame_trigger.empty() )
            {
                camera.TriggerSelector.SetValue( Basler_GigECamera::TriggerSelector_FrameStart );
                camera.TriggerMode.SetValue( Basler_GigECamera::TriggerMode_Off );
            }
            else
            {
                camera.TriggerSelector.SetValue( Basler_GigECamera::TriggerSelector_FrameStart );
                camera.TriggerMode.SetValue( Basler_GigECamera::TriggerMode_On );
                Basler_GigECamera::TriggerSourceEnums t;
                if( frame_trigger == "line1" ) { camera.TriggerSource.SetValue( Basler_GigECamera::TriggerSource_Line1 ); }
                if( frame_trigger == "line2" ) { camera.TriggerSource.SetValue( Basler_GigECamera::TriggerSource_Line2 ); }
                if( frame_trigger == "line3" ) { camera.TriggerSource.SetValue( Basler_GigECamera::TriggerSource_Line3 ); }
                else if( frame_trigger == "encoder" ) { camera.TriggerSource.SetValue( Basler_GigECamera::TriggerSource_ShaftEncoderModuleOut ); }
                else { std::cerr << "basler-cat: frame trigger '" << frame_trigger << "' not implemented or invalid" << std::endl; return 1; }
                camera.TriggerActivation.SetValue( Basler_GigECamera::TriggerActivation_RisingEdge );
                camera.TriggerSelector.SetValue( Basler_GigECamera::TriggerSelector_LineStart );
                camera.TriggerMode.SetValue( Basler_GigECamera::TriggerMode_On );
                camera.TriggerActivation.SetValue( Basler_GigECamera::TriggerActivation_RisingEdge );
                if( frame_trigger == "encoder" )
                {
                    // todo: make configurable
                    camera.ShaftEncoderModuleLineSelector.SetValue( Basler_GigECamera::ShaftEncoderModuleLineSelector_PhaseA );
                    camera.ShaftEncoderModuleLineSource.SetValue( Basler_GigECamera::ShaftEncoderModuleLineSource_Line1 );
                    camera.ShaftEncoderModuleLineSelector.SetValue( Basler_GigECamera::ShaftEncoderModuleLineSelector_PhaseB );
                    camera.ShaftEncoderModuleLineSource.SetValue( Basler_GigECamera::ShaftEncoderModuleLineSource_Line2 );
                    camera.ShaftEncoderModuleCounterMode.SetValue( Basler_GigECamera::ShaftEncoderModuleCounterMode_FollowDirection );
                    camera.ShaftEncoderModuleMode.SetValue( Basler_GigECamera::ShaftEncoderModuleMode_ForwardOnly );
                    camera.ShaftEncoderModuleCounterMax.SetValue( encoder_ticks - 1 );
                    /// @todo compensate for mechanical jitter, if needed
                    ///       see Runner_Users_manual.pdf, 8.3, Case 2
                    camera.ShaftEncoderModuleReverseCounterMax.SetValue( 0 );
                    camera.ShaftEncoderModuleCounterReset.Execute();
                    camera.ShaftEncoderModuleReverseCounterReset.Execute();
                }
            }
        }
        GenApi::IEnumEntry* lineStart = camera.TriggerSelector.GetEntry( Basler_GigECamera::TriggerSelector_LineStart );
        if( lineStart && GenApi::IsAvailable( lineStart ) )
        {
            if( line_trigger.empty() )
            {
                camera.TriggerSelector.SetValue( Basler_GigECamera::TriggerSelector_LineStart );
                camera.TriggerMode.SetValue( Basler_GigECamera::TriggerMode_Off );
            }
            else
            {
                camera.TriggerSelector.SetValue( Basler_GigECamera::TriggerSelector_LineStart );
                camera.TriggerMode.SetValue( Basler_GigECamera::TriggerMode_On );
                Basler_GigECamera::TriggerSourceEnums t;
                if( line_trigger == "line1" ) { camera.TriggerSource.SetValue( Basler_GigECamera::TriggerSource_Line1 ); }
                else if( line_trigger == "line2" ) { camera.TriggerSource.SetValue( Basler_GigECamera::TriggerSource_Line2 ); }
                else if( line_trigger == "line3" ) { camera.TriggerSource.SetValue( Basler_GigECamera::TriggerSource_Line3 ); }
                else if( line_trigger == "encoder" ) { camera.TriggerSource.SetValue( Basler_GigECamera::TriggerSource_ShaftEncoderModuleOut ); }
                else { std::cerr << "basler-cat: line trigger '" << line_trigger << "' not implemented or invalid" << std::endl; return 1; }
                camera.TriggerActivation.SetValue( Basler_GigECamera::TriggerActivation_RisingEdge );
                camera.TriggerSelector.SetValue( Basler_GigECamera::TriggerSelector_LineStart );
                camera.TriggerMode.SetValue( Basler_GigECamera::TriggerMode_On );
                camera.TriggerActivation.SetValue( Basler_GigECamera::TriggerActivation_RisingEdge );
            }
        }
        if( chunk_mode )
        {
            std::cerr << "basler-cat: setting chunk mode..." << std::endl;
            if( !GenApi::IsWritable( camera.ChunkModeActive ) ) { std::cerr << "basler-cat: camera does not support chunk features" << std::endl; camera.Close(); return 1; }
            camera.ChunkModeActive.SetValue( true );
            camera.ChunkSelector.SetValue( Basler_GigECameraParams::ChunkSelector_Framecounter );
            camera.ChunkEnable.SetValue( true );
            camera.ChunkSelector.SetValue( Basler_GigECameraParams::ChunkSelector_Timestamp );
            camera.ChunkEnable.SetValue( true );
            camera.ChunkSelector.SetValue( Basler_GigECameraParams::ChunkSelector_LineTriggerIgnoredCounter );
            camera.ChunkEnable.SetValue( true );
            camera.ChunkSelector.SetValue( Basler_GigECameraParams::ChunkSelector_FrameTriggerIgnoredCounter );
            camera.ChunkEnable.SetValue( true );
            camera.ChunkSelector.SetValue( Basler_GigECameraParams::ChunkSelector_LineTriggerEndToEndCounter );
            camera.ChunkEnable.SetValue( true );
            camera.ChunkSelector.SetValue( Basler_GigECameraParams::ChunkSelector_FrameTriggerCounter );
            camera.ChunkEnable.SetValue( true );
            camera.ChunkSelector.SetValue( Basler_GigECameraParams::ChunkSelector_FramesPerTriggerCounter );
            camera.ChunkEnable.SetValue( true );
            parser = camera.CreateChunkParser();
            if( !parser ) { std::cerr << "basler-cat: failed to create chunk parser" << std::endl; camera.Close(); return 1; }
            std::cerr << "basler-cat: set chunk mode" << std::endl;
        }
        camera.ExposureMode.SetValue( Basler_GigECamera::ExposureMode_Timed );
        if( vm.count( "exposure" ) ) { camera.ExposureTimeRaw.SetValue( exposure ); } // todo? auto exposure (see ExposureAutoEnums)
        if( vm.count( "gain" ) )
        { 
            camera.GainSelector.SetValue( Basler_GigECamera::GainSelector_All );
            camera.GainRaw.SetValue( gain );
            if( channels == 3 ) // todo: make configurable; also is not setting all not enough?
            {
                camera.GainSelector.SetValue( Basler_GigECamera::GainSelector_Red );
                camera.GainRaw.SetValue( gain );
                camera.GainSelector.SetValue( Basler_GigECamera::GainSelector_Green );
                camera.GainRaw.SetValue( gain );
                camera.GainSelector.SetValue( Basler_GigECamera::GainSelector_Blue );
                camera.GainRaw.SetValue( gain );
            }
        }
        if( vm.count( "line-rate" ) ) { camera.AcquisitionLineRateAbs.SetValue( line_rate ); }
        if( vm.count( "test-colour" ) ) { camera.TestImageSelector.SetValue( Basler_GigECamera::TestImageSelector_Testimage6 ); }
        else { camera.TestImageSelector.SetValue( Basler_GigECamera::TestImageSelector_Off ); }
        unsigned int payload_size = camera.PayloadSize.GetValue();
        if( verbose )
        { 
            std::cerr << "basler-cat: camera mtu size: " << camera.GevSCPSPacketSize.GetValue() << std::endl;
            std::cerr << "basler-cat: exposure: " << camera.ExposureTimeRaw.GetValue() << std::endl;
            std::cerr << "basler-cat: payload size: " << payload_size << std::endl;
        }
        std::vector< std::vector< char > > buffers( 2 ); // todo? make number of buffers configurable
        for( std::size_t i = 0; i < buffers.size(); ++i ) { buffers[i].resize( payload_size ); }
        grabber.MaxBufferSize.SetValue( buffers[0].size() );
        grabber.SocketBufferSize.SetValue( 127 );
        if( verbose )
        { 
            std::cerr << "basler-cat: socket buffer size: " << grabber.SocketBufferSize.GetValue() << std::endl;
            std::cerr << "basler-cat: max buffer size: " << grabber.MaxBufferSize.GetValue() << std::endl;
        }
        grabber.MaxNumBuffer.SetValue( buffers.size() ); // todo: use --buffer value for number of buffered images
        grabber.PrepareGrab(); // image size now must not be changed until FinishGrab() is called.
        std::vector< Pylon::StreamBufferHandle > buffer_handles( buffers.size() );
        for( std::size_t i = 0; i < buffers.size(); ++i )
        { 
            buffer_handles[i] = grabber.RegisterBuffer( &buffers[i][0], buffers[i].size() );
            grabber.QueueBuffer( buffer_handles[i], NULL );
        }
        if( chunk_mode )
        {
            snark::tbb::bursty_reader< ChunkPair > read( boost::bind( &capture_< ChunkPair >, boost::ref( camera ), boost::ref( grabber ) ), discard );
            tbb::filter_t< ChunkPair, void > write( tbb::filter::serial_in_order, boost::bind( &write_, _1 ) );
            snark::tbb::bursty_pipeline< ChunkPair > pipeline;
            camera.AcquisitionMode.SetValue( Basler_GigECamera::AcquisitionMode_Continuous );
            camera.AcquisitionStart.Execute(); // continuous acquisition mode        
            if( verbose ) { std::cerr << "basler-cat: running in chunk mode..." << std::endl; }
            pipeline.run( read, write );
            if( verbose ) { std::cerr << "basler-cat: shutting down..." << std::endl; }
            camera.AcquisitionStop();
            camera.DestroyChunkParser( parser );
        }
        else
        {
            snark::cv_mat::serialization serialization( options );
            snark::tbb::bursty_reader< Pair > reader( boost::bind( &capture_< Pair >, boost::ref( camera ), boost::ref( grabber ) ), discard );
            snark::imaging::applications::pipeline pipeline( serialization, filters, reader );
            camera.AcquisitionMode.SetValue( Basler_GigECamera::AcquisitionMode_Continuous );
            camera.AcquisitionStart.Execute(); // continuous acquisition mode        
            if( verbose ) { std::cerr << "basler-cat: running..." << std::endl; }
            pipeline.run();
            if( verbose ) { std::cerr << "basler-cat: shutting down..." << std::endl; }
            camera.AcquisitionStop();
        }
        if( verbose ) { std::cerr << "basler-cat: acquisition stopped" << std::endl; }
        is_shutdown = true;
        while( !done ) { boost::thread::sleep( boost::posix_time::microsec_clock::universal_time() + boost::posix_time::milliseconds( 100 ) ); }
        grabber.FinishGrab();
        Pylon::GrabResult result;
        while( grabber.RetrieveResult( result ) ); // get all buffers back
        for( std::size_t i = 0; i < buffers.size(); ++i ) { grabber.DeregisterBuffer( buffer_handles[i] ); }
        grabber.Close();
        camera.Close();
        if( verbose ) { std::cerr << "basler-cat: done" << std::endl; }
        return 0;
    }
    catch( std::exception& ex ) { std::cerr << "basler-cat: " << ex.what() << std::endl; }
    catch( ... ) { std::cerr << "basler-cat: unknown exception" << std::endl; }
    return 1;
}