C _Third_Party - RicoJia/notes GitHub Wiki

========================================================================

Debugging Tools

========================================================================

  1. Clang-tidy
    • running static analysis code check. Override,e.g,

======================================================================== Nanopb

  1. Nanopb input stream: Link
    • keep calling read_callback until eof
    • stream->store can be used to store user data. Next time we call input, it will be flushed automatically for the next incoming connection ========================================================================

ffmpeg

========================================================================

  1. Basics

    • Encoding: 1. camera -> RGB 2. RGB to h264 (YUV encoding) 3. h264 to FLV 4. FLV to RTMP for CDN (CDN is the streaming server)

    • Initialization

          extern "C"{
            #include "lib..."   // we need extern, because C doesn't support overloading. Without "extern", C++ will add function signature to function names, which is not what C does. Then, C cannot find the function.
          }
    • muxing: multiple parallel sources being serialized - MP4, WEBM... they're "containers" that contain audio and video, and subtitles. And they're compressed into that container (mux). - Your media player will decompress them (demux)

    • ffplay -flags2 +export_mvs input.mp4 -vf codecview=mv=pf+bf+bb

  2. Initialization:

    1. formats
        AVInputFormat, AVOutputFormat are container types, like MPEG4.
        av_register_all(): flv, mp4. mov... all of them
      
    • Codec: encode and decode, happens after muxing / demuxing.
    1. open all formats:

          // Context, which subsequent operations will depend on this. That includes format and io (file io or internet io)
          AVFormatContext *ictx;            
      
          const char* inUrl = "rtsp://..."; // or "...mp4"
          
          // fmt: input container. Null means no designated input container. System will tell from file suffix?
          AVInputFormat* fmt = NULL; 
          AVDictionary **options = NULL;    //no need for NULL
      
          int ret = avformat_open_input(AVFormatContext** ps  = &ictx, const char* inUrl, AVInputFormat *fmt);    
      
          // For h254, flv, stream info (fps, height...) are in every individual frame. 
          av_find_stream_info(ictx,0);    // here ictx has been allocated memory, so we pass it in directly
      
      
      • in C, if taking in a ptr to ptr, it is to allocate memory
      • For convenience, add getchar() to pause for debugging
    2. print stream info

      av_dump_format(ictx, 0 ,inUrl, )
      
      • stereo is stereo audio

Create output stream

  ```
    AVFormatContext *octx = NULL; 
    AVOutputFormat *oformat = NULL; 
    const char* outUrl = "rtmp://192...."
    // allocate memory to output context
    avformat_alloc_output_context2(&octx, oformat, "flv", outUrl);    // we specify "flv" because outUrl does not specify the format 
    if (!octx){cout<<error;}    

  ```
  - Technically, within a process, you should immediately release resources if you're done. Else, when exiting, some resources will be released at a delayed time
  1. AVFormatContext explaination

      AVFormatContext{
        AVStream **streams;     //multiple video+audio streams
        int nb_streams;   //number of streams
      }
      AVStream{
        AVRational time_base;     //time stamp?
        AVCodecParameters codecpar;     // parameters, you can also know if this is audio/subtitles/vidoe from here
        AVCodecContext *codec;      // 
      }
    
  2. Then all streams in AVStream will be read. link

  3. Misc

  4. bit rate: fixed bit rate is preferred; adaptive bit rate is: when frames don't change much, h264 doesn't require high bit rate

  5. bi-lateral filtering (better than Gaussian Blurring)

  6. cmake: to successfully compile ffmpeg for a shared_object, ffmpeg needs to be a shared object.

    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -O3 -fPIC")
    set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -O3 -fPIC")
    add_definitions(${GCC_COMPILE_FLAGS})
    
    include_directories("${FFMPEG_DIR}/include")
    LINK_DIRECTORIES("${FFMPEG_DIR}/lib/")
    # to use static lib ffmpeg for our shared lib
    set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-Bsymbolic")
    
    target_link_libraries(obj PUBLIC
    # ffmpeg libs, you need all these! -l are system libraries
    avdevice avformat avfilter avcodec avutil avresample swresample postproc swscale
    -lmp3lame -lX11 -lvdpau -lva-x11 -lstdc++fs
    -lssl -lcrypto -ldl
    )
    

Read stream into packets

  1. av_read_frame()

      // contains: 
      //  1. stream_index: which stream to read from 
      //  2. pts: pts * (num/den) is which second will this frame be played. num, den are stored in time_base? p is for print
      //  3. there's pframe, bframe, and keyframe in H264. keyframe is a complete frame; pframe is the difference from the last frame; bframe is the difference from the next frame, so you need to decode the next frame first, then display (b is optional)
      //  bframe may provide higher compression ratio, cuz the changes might be smaller?
      // dts if for bframe, d is to decode
      AVPacket pkt; 
    
  2. av_packet_free(&packet);

  • If you don't use this packet, free it. This is an old function
  • av_packet_unref is to "unreference the packet", and ffmpeg will free the packet automatically when ref is 0.
  1. rtsp protocol: like rtmp, but used in surveillance. RTMP used in internet streaming.

  2. decoding time stamp (DTS) and a presentation time stamp (PTS)

    • PTS is which frame the current frame should be from the initial frame. For example, if a stream has 24 frames per second, a PTS of 42 is going to indicate that the frame should go where the 42nd frame would be if there we had a frame every 1/24 of a second

Decoding

  1. this error just means it hasn't seen a keyframe yet:
  non-existing PPS 0 referenced
  decode_slice_header error
  no frame!

========================================================================

libJpeg

========================================================================

  1. libjpeg works with the decoded packet, which is [R, G, B, R, G, B ...]
  2. Compilation
    1. <stdio.h> and <stdlib.h> must be included, and must before "jpeglib.h"
    2. in CMakeLists.txt, must have target_link_libraries(util -ljpeg)
    3. after having a .c file, have set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fpic -DBUILD_JPEG=ON")

========================================================================

Pybind

========================================================================

  1. example_project

Build

  1. you need sudo apt-get install libpython2.7-dev python-numpy to install python properly
    • lib.so is what you need
  2. key elements
#include <pybind11/pybind11.h>

#pragma GCC visibility push(hidden)
namespace py = pybind11;

#pragma GCC visibility pop

PYBIND11_MODULE(example, m) {
    // optional module docstring
    m.doc() = "pybind11 example plugin";

    // define add function, name
    m.def("add_py", &add, "A function which adds two numbers");

    // bindings to Pet class
    py::class_<Pet>(m, "Pet")
        .def(py::init<const std::string &, int>())    //this is the initializer
        .def("go_for_a_walk", &Pet::go_for_a_walk)
        .def("get_hunger", &Pet::get_hunger)
        .def("get_name", &Pet::get_name);
}
  • in a test, check if something exists
    assert 'codec' in packet
    assert 'buf' in packet
    
  1. Mixed interface, keyword based but also partial list? Pattern:

    • can specify default values at will, and partial args can be passed into the list.
    • Without keywords, the args will be matched to the first param that can be converted
    • With, keywords then the corresponding param will take the arg
    • If any param is left with no value, then there's error
    • Mixed keywords with positional args:
      • positional args CANNOT follow keyworded args
      • Positional args can go before the keyworded args
  2. Errors

    • ImportError: dynamic module does not define init function, see here. Make sure the names match in pybind11_module!!
    • segfault when importing, and there's no .cpython.so - compiled against python2 - Solution, inpired from here, simply specify 3.6 find_package(PythonInterp 3.6 REQUIRED) . - If you see Could NOT find PythonInterp: Found unsuitable version "2.7.17", delete build and rebuild
    • pybind11 defined with greater visibility:
      • without GCC visibility push(hidden)make sure your class doesn't have pybind11 objects. as class members
      • or add GCC visibility push(hidden) and GCC visibility pop()
  3. add args:

    py::class_<Connector>(m, "connector")
        .def(py::init<uint32_t, std::string, std::string, Type, uint32_t, uint32_t>(), py::arg("port"), py::arg("ip"),
             py::arg("topic"), py::arg("type"), py::arg("heartbeat") = 10, py::arg("queuesize") = 10)
    
    // implementation
    // def(func, Extra ... extra)
    // operator = (const py::arg&) ...

Containers

  1. General Notes
    • Whenever manipulating Python objects (even reference counting, and definitely when destroying it), the GIL needs to be held, also when it's implicit at the end of a scope.
    • someone said gil must be acquired
    • one principle: py:object can be casted!
    • Everything extracted from py::object is a reference
      void pybind_test(py::list& ls){
            ls.append(py::array_t<int>({2,3}));
            auto arr = ls[ls.size()-1].cast<py::array_t<int>>();
            int* ptr = (int*) arr.request().ptr;
            ptr[0] = 999; 
            ptr[3] = 666;
      }
      //python will see 999 and 666.

Small data types

  1. py::str to regular string: obj.caststd::string();
  2. tuple
py::tuple args = py::make_tuple(1234, "hello", some_instance);
  1. operations
  2. common way for copying from py::array_t to a structure: 1. get size of the array: py::array_t<uint32_t> arr; uint32_t size = arr.shape(0) * arr.ndim(); Foo* f = new Foo(); memcpy(f, arr.data(), size);

Py::array

  1. buffer protocols: many objects satisfies this?
  • py::array
    #include <pybind11/numpy.h>
    void f(py::array_t<double> array);
  • check py::array dim, shape:
    py::buffer_info buf = array.request();
    buf.ndim
  • vectorize: easier operation on an element (but it's for interfacing with python only, not internal uses)
    double my_func(int x, float y, double z);
    m.def("vectorized_func", py::vectorize(my_func));
    
    // python
    x = np.array([[1, 3],[5, 7]])
    y = np.array([[2, 4],[6, 8]])
    z = 3
    result = vectorized_func(x, y, z)
  1. std::vector to pyarray
    template <typename T>
    py::array_t<T> vector1D_to_numpy_array(const std::vector<T>& vec){
        py::array_t<T> arr(vec.size()); 
        auto arr_buffer = arr.request(); 
        double* arr_ptr = (T*) arr_buffer.ptr; 
        
        std::memcpy(arr_ptr, vec.data(), vec.size() * sizeof(T)); 
        return arr; 
    }
  2. multi-dimensional array:
    1. Create a multi-dimensional array
      py::array_t<double> pybind_test(){
          py::array_t<double> arr(std::vector<ptrdiff_t>{2, 3, 4});
          auto arr_buffer = arr.request();
          double* arr_ptr = (double*) arr_buffer.ptr;
      
          arr_ptr[0] = 100;
          arr_ptr[1] = 200;
          arr_ptr[2] = 300;
          return arr;
      }
      • how to specify strides? No need
      • can work with float
    2. How to print out a 2d py::array, when you can't vectorize
      void pybind_test(const py::dict& dict){
         auto P = dict["surf_points"].cast<py::array_t<float>>();  
         py::buffer_info buf = P.request(); 
        int X = buf.shape[0];
        int Y = buf.shape[1];
        float *ptr = static_cast<float *>(buf.ptr);
        cout<<X<<" | "<<Y<<endl;
        for(int i = 0; i < X; ++i)
          for(int j = 0; j < Y; ++j)
            cout<<ptr[i * Y + j]<<endl;
      }
  3. <uint8_t*> array.request().ptr requires that <uint8_t*>, because even though array's type has been deduced, pybind will still return void* due to C constraints

Py::list

  1. py::list

    py::list pybind_test(){
      py::list ls;
      ls.append(1);
      ls.append("joj");
      return ls;
    }
    • std::vector and py::list can convert back and forth as well
    • py::list doesn't support list[-1]
  2. py::list::size

    void test(py::list l) {
        l.attr("pop")();
        std::cout << "List has length " << l.size() << std::endl;
  3. read a 2d py::list

    void pybind_test(const py::list& ls){
          cout<<ls.size()<<endl;
          for (const auto& item : ls){
              auto sub_list  = item.cast<py::list>();
              for (const auto& it : sub_list){
                cout<<it.cast<int>()<<endl;
              }
          }
    }
  4. pybind11, call a method of py::list:

    list.attr("clear")()

py dict

  1. py::dict:

    dict.get(key) = dict[key]
    dict.get(non_existent_key, val)   #return val
    • operations
      1. iterate cpp void print_dict(py::dict dict) { /* Easily interact with Python types */ for (auto item : dict) std::cout << "key=" << std::string(py::str(item.first)) << ", " << "value=" << std::string(py::str(item.second)) << std::endl; }
      2. read directly cpp bool yes = dict["yes"].cast<bool>()
    • py::dict to std::map<std::string, float>
      void load_map_python_dict(const py::dict& python_dict, std::map<std::string, float> &outMap) {
          for(const std::pair<py::handle, py::handle>& item: python_dict){
              auto key = item.first.cast<std::string>();
              auto value = item.second.cast<float>();
              outMap.at(key) = value;
          }
      }
  2. write to dictionary

    • ! quirk
      class Foo{
          private: 
              py::dict di_; 
          public: 
              void test_foo(){
                  // has to be "foo" (const char*), cannot be std::string
                  di["foo"] = bar;
              }
              std::thread th_;
      }; 
      // **!** It does look like we are modifying different dictionary on different threads
      // but there might be data corruption
      // so you need a cpp lock
      std::vector<Foo> foo_vec (3); 
      for (auto& foo: foo_vec){
         foo.th_ = std::thread(&Foo::test_foo, &foo) ; 
      }
      for (auto& foo: foo_vec){
         foo.th_.join() ; 
      }
    • put py::arr into py::dict
      py::dict pybind_test(){
          std::vector<double> vec {1.0, 2.0, 3.0};
          auto vec_arr =  vector1D_to_numpy_array(vec); 
      
          py::gil_scoped_acquire acquire;
          py::dict return_dict;
          return_dict["hee"] = vec_arr; 
          py::gil_scoped_release release;
          return return_dict; 
      };
  3. Get stuff out of py::dict: you need to cast everything, once getting an object from a dictionary

      py::array_t<uint8_t> input = arcomm_msg["raw_data"].cast<py::array_t<uint8_t>>();

Exceptions

  1. wrapping C++ exceptions into python exceptions.

    class CppExp : public std::exception{
      
      public:  
        CppExp(const std::string& msg): msg_(msg){}
        virtual char const* what() const noexcept {return msg_.c_str();}
      private: 
        std::string msg_; 
    };
    
    throw CppExp("lol"); 
    
    PYBIND11_MODULE(example, m) { //module name must match file name
        py::register_exception<CppExp>(module, "PyExp");
    }
    
    // in python: 
    try:
      test.test1.fn(...)
    except test.PyExp as ex:
      print("exception error.", ex)
  2. How to compare / set numpy flags:

    py::detail::array_proxy(array.ptr())->flags &=~py::detail::npy_api::NPY_ARRAY_OWNDATA_;
    arr = np.array([1.0, 2.0])
    arr.setflags(write=0, align=0)

Functions

  1. callback:

    • recommended version good example
      m.def("test_unpacking_error1", [](const py::function &f) {
          auto kwargs = py::dict("x"_a=3);
          return f("x"_a=1, "y"_a=2, **kwargs); // duplicate ** after keyword
      });
    • longer version. ! to make it cpp safe, we need to have a lock to protect the gil!
      //Python
      def cb (dict):
          print(dict)
      foo.registerCallback(batch_reporter_callback)
      
      // cpp
      using Callback = std::function<void(py::dict)>;
      void registerCallback(Callback cb){
        cb_ = cb;
      }
      PYBIND11_MODULE(Foo, m)
      {
        py::class_<Foo>(m, "Foo")
          .def("registerCallback", &Foo::registerCallback, "doc");
      };
      
      // lock to protect gil object, since it's not cpp thread safe
      std::unique_lock<std::mutex> ul(mtx); 
      py::gil_scoped_acquire acquire;
      cb_(dict);
      py::gil_scoped_release release;
  2. Do we need custom deleter for del in python? - No.

  3. Error undefined reference to `PyExc_RuntimeError' is caused by lacking target_link_libraries(gtest_util ${PYTHON_LIBRARY})

  4. For template functions, instantiate them like

    template <typename T> void check_if_array_contiguous(const py::array_t<T>& array) {
    }
    m.def("check_if_array_contiguous", check_if_array_contiguous<int>);
    • Enable keyword arg with default args for keyword args: note the default args don't have to be the last!!
      .def("create_plane", &MultipleCameraProjector3::create_plane,  py::arg("x0") = 4,
                                                                     py::arg("y0"),
                                                                     py::arg("flip_normal")=false
                                                                     );
      • in python
      create_plane(y0 = 1)

Multi-threading

  1. Python just is just like a single-core processor doing context switching.

  2. GIL is held by Python threadwhen you go to C++ python and comes back to Python,

    1. do py::gil_scoped_release release; to release the lock into cpp space. Then acquire gil when you need to interact with python
      • check source code, this is RAII, so it's valid only for that space
    2. check GIL held status
    auto report_gil_status = []() {
        auto is_gil_held = false;
        if (auto tstate = py::detail::get_thread_state_unchecked())
            is_gil_held = (tstate == PyGILState_GetThisThreadState());
    
        return is_gil_held ? "GIL held" : "GIL released";
    };
    std::cout<<__FUNCTION__<<"gil held: "<<report_gil_status()<<std::endl;
  3. You need to use gil for creating py::dict

      py::gil_scoped_acquire acquire; 
      py::dict di; 
      di["he"] = 1; 
      py::gil_scoped_release release; 
  4. running pybind in cpp main, not initiated by python willr require py::scoped_interpreter:

    #include <pybind11/embed.h>
    namespace py = pybind11;
    using namespace py::literals;
    int main() {
        py::scoped_interpreter guard{};
    
        auto locals = py::dict("name"_a="World", "number"_a=42);
        }
    • problem: having trouble launching py::module::import on a different thread. py::dict is fine
  5. pickle_ = py::module::import("pickle"); module is a singleton, so when created by multiple cpp, there'd be a segfault.

    • When GIL is held by one thread, while the other thread is trying to access py::object. Then you'll see fatal-error-gc-object-already-tracked.
    • You can create multiple py::module, but they're references to the same thing.
  6. GIL is needed cuz CPython's memory management is not thread-safe.

Quirks

  1. how to pass an argument by reference? no workaround (as some inputs are immutable in python)

  2. How to generate a reference and use it properly?

    • for string, a new py::string (so it's a copy) is created,
    • for STL containers, you should make_opaque before passing the reference.
  3. Pybind quirk: copy construction is never real "copy construction" - always pushes a reference to it

    dict["lol"] = another_dict
    another_dict["foo"]  = "dummy"  #make some modifications
    dict["bar"] = another_dict      #you will see the last modifications only, because copy construction is actually reference.
  4. pybind 11

    • Do not initialize a pybind object during cpp module ctor initializer, I think the GIL is not available in ctor initializer
  5. Not linking all the libs is the root cause of ImportError: /srv/ocean/server/test/feeder_test/multi_streamer.so: undefined symbol: _ZN4util11PacketSaverD1Ev

    class Foo{
      UtilClass; 
    };
    // UtilClass.hpp is there, but UtilClass.cpp is Not defined, or not linked to hpp!!
  6. Testing Tips

    1. pybind 11 - best test with python
    2. pybind 11 - for ctor, dictionary is better than list, etc. when developing, especially you don't know what eventually things will be.
  7. Reminders

    1. m.def("write_pickle_to_bag_multithreaded", write_pickle_to_bag_multithreaded);: m.def(Function_name, func_in_cpp), not documentation!!
  8. Common Errors:

    1. error: see AttributeError: module 'build.example' has no attribute 'start', check if we have a mismatch: m.def("pybind_test", &start, "A function which adds two numbers");

    2. error

      TypeError: register_cb(): incompatible function arguments. The following argument types are supported:
      1. (self: build.example.Foo) -> None
      
      • the callback is Foo::register_cb(). None means the return type of Foo::register_cb() is void
      • The reason might be forgetting to include pybind11/functional.h
    3. if something is like [None], then it might raise an error: " RuntimeError: Unable to cast Python instance to C++ type (compile in debug mode for details)"

========================================================================

YAML

========================================================================

  1. What is yaml-cpp?
    • yaml string: what is yaml about? map? (a state machine that acts like std::ostream)
  2. Small Nuisances:
    • yaml_cpp: Pay attention to spacing: emit << YAML::Key << " topics" << YAML::Value << YAML::BeginSeq; will print " topics" instead of topics.
    • also, "" empty string doesn't work here.
  3. use https://onlineyamltools.com/validate-yaml to validate YAML.
  4. YAML-cpp template
    YAML::Emitter out;
    out << YAML::Key << "camera_calibration" << YAML::Value << YAML::BeginSeq;
    out << YAML::BeginMap;
    out << YAML::Key << "before camera_id" << YAML::Value << cf->camera_id;
    
    out << YAML::EndSeq;
    out << YAML::EndMap;
    out << YAML::Key << "camera_frame_info" << YAML::Value;
    out << YAML::BeginMap;
    out << YAML::Key << "shelf_id" << YAML::Value << cfi.shelf_id;
    out << YAML::EndMap;

========================================================================

Picklingtool

========================================================================

  1. make will not compile, and spits out: cannot open shared object file: No such file or directory. The problem is the space after "load" at line 61. Change "load :" to "load:"
  2. CMakeLists.txt: add_compile_options(-DOC_NEW_STYLE_INCLUDES) #for include<iostream.h>
  3. What versions of Python does PicklingTools support? 3.x has not been tested.
  4. Pickled arrays in python2 uses a different encoding than in Python 3. In Python3, the receiving end's encoding should be like: pickle.load(file, encoding='latin1')

Data types

Array

  1. array: ALWAYS constructed empty, with an initial capacity. need to either fill the Array , or append to the Array.
    • Will automatically doubles the capacity and copies all the old data into the resized memory if file size exceeded.
      Array<real_8> demod_data(10); // Initial empty: Reserve space for 10 elements
      demod_data.fill(0.0);         // Fill to capacity (10) with 0.0
      for (int ii=0; ii<demod_data.length(); ii++) {
         demod_data[ii] = demod_data[ii] + ii;
      }
    • contiguous memory
      Array<char> a(5);
      a.fill('\0');
      char* data = a.data();  // Returns &a[0]
      strcpy(data, "hi");     // expect contiguous piece of memory
  2. fill an array: it's dynamic array
    //the slower way: 
    Array<uint8_t> arr; 
    arr.append(128);
    
    // the faster way: 
    Array<uint8_t> arr(3); 
    uint8_t temp[] = {128, 255, 230};
    arr.fill(0);    //initialize memory
    std::memcpy(arr.data(), temp, 3*sizeof(uint8_t)); 
    
    // a list: 
    Arr attachment_keys = "['h264']";
    dict.insertKeyAndValue("attachment_keys", attachment_keys);

Tab

  1. A Tab is python dictionary
    • create and iterator:
      Tab t;                                // Empty table
      Tab t = "{ 'a': 1, 0:'something'}";   // Table with 2 key-value pairs
      for (It ii(t); ii(); ) {              // Iterate through table
         cout << ii.key() << ii.value();
      }

========================================================================

Eigen

======================================================================== 0. Installations. see code

  1. Matrix3t: t can be i (integer), d (double), f(float), see here

    #include <Eigen/Eigen>
  2. MatrixXd needs dynamic allocation

  3. Basic Operations see code

    1. Creation (Same with vectors),

      • vectorXd can work with X up to 4.
      • mat.block<2,2>(0,0) can be used to change the value of a block
      • col normalized. see code
      • **Caution: ** do not use auto, initialize like Eigen::Matrix3d R_3d = Eigen::Matrix3d::Identity();
    2. Basic Operations

      M1 + M2; 
      M1 * M2;    //Matrix Multiplication
      M2 - Matrix4d::Ones() * 2.2 == Matrix4d::Zero(); 
      M2.transpose(); 
      M1.inverse(); 
      M1.trace(); 
      M1.sum(); 
      M1.minCoeff();    // minimal coefficient
      • Vectors
        vec.norm();     //L2 norm
        vec.squaredNorm();   //L2 Norm ^2 
        vec.dot(vec2); 
        vec.normalized(); 
        vec.cross(vec2); 
    3. Element-wise Operations

      // Square each element 
      M1.array().square(); 
      M1.array() * Matrix4f::Identity().array(); 
      M1.array() <= M2.array();     //element-wise comparison
      • vectors
        vec.array().abs(); 
        array.matrix(); //convert back to matrix
      • head, tail
      • access elements
    4. Transforms (can be used on rotation, etc),see here

  4. Advanced Operations

    • Block operations
      Array22f m;
      m << 1,2,
         3,4;
      Array44f a = Array44f::Constant(0.6);
      
      cout << "Here is the array a:" << endl << a << endl << endl;
      // see 
      Here is the array a:
      0.6 0.6 0.6 0.6
      0.6 0.6 0.6 0.6
      0.6 0.6 0.6 0.6
      0.6 0.6 0.6 0.6
      
      a.block<2,2>(1,1) = m;
      cout << "Here is now a with m copied into its central 2x2 block:" << endl << a << endl << endl;
      
      // see 
      Here is now a with m copied into its central 2x2 block:
      0.6 0.6 0.6 0.6
      0.6   1   2 0.6
      0.6   3   4 0.6
      0.6 0.6 0.6 0.6
  5. Errors:

    • when you have auto Mat = a * b; mat2 = a * Mat see errors, do mat2 = a * Mat.matrix()

========================================================================

Sophus

========================================================================

  1. Examples
⚠️ **GitHub.com Fallback** ⚠️