for Robot Artificial Inteligence

10.template, Iterator, Error Handling, Program input parameter, Opencv

|

Generic Programming

  • Generic programming: separate algorithms from the data type
  • Cup holds any type T, e.g. Coffee or Tea

Template functions

  • Generic programming uses keyword template
    template <typename T, typename S>
    T awesome_function (const T& var_t , const S& var_s) {
    // some dummy implementation
    T result = var_t;
    return result;
    }
    
  • T and S can be any type that is:
    • Copy constructable
    • Assignable
    • Is defined (for custom classes)

Explicit type

  • If the data type cannot be determined by the compiler, we must define it ourselves
template <typename T>
T DummyFuncion(){
  T result;
  return result;
}
int main()
{
  DummyFuncion <int>();
  DummyFuncion <double>();
  return 0;
}

Template Classes

  • Similar syntax to template functions
  • Use template type anywhere in class
    template <class T>
    class MyClass
    {
    public:
    Myclass(const T& smth) : smth_{smth}
    private:
    T smth_;
    }
    int main()
    {
    MyClass<int> my_object(10);
    MyClass <double> my_double_object(10.0);
    return 0;
    }
    

Template specialisation

  • We can specialize for a type
  • Works for functions and classes alike
// Function definition
template <typename T>
T DummyFuncion(){
  T result;
  return result;
}
template <> // all give what i want type
int DummyFuncion(){ return 42;}
int main()
{
  DummyFuncion <int>();
  DummyFuncion <double>();
  return 0;
}

Template meta programming

  • Templates are used for Meta programming
  • The compiler will generate concrete instances of generic classes based on the classes we want to use
  • If we create MyClass and MyClass the compiler will generate two different classes with appropriate types instead of template parameter

Template classes headers/source

  • Concrete template classes are generated instantiated at compile time
  • Linker does not know about implementation
  • There are three options for template classes:
    • Declare and define in header files
    • Declare in NAME.h file, implement in NAME.hpp file, add **#include ** in the end of **NAME.h**
    • Declare in * .h file, implement in * .cpp file, in the end of the * .cpp add explicit instantiation for types you expect to use
  • Read More about it https://en.cppreference.com/w/cpp/language/class_template https://www.drdobbs.com/moving-templates-out-of-header-files/184403420

    Iterators

    STL uses iterators to access data in containers

  • Iterators are similar to pointerss
  • Allow quick navigation through containers
  • Most algorithms in STL use iterators
  • Access current element with * iter
  • Accepts -> alike to pointers
  • Move to next element in container iter++
  • Prefer range-based for loops
  • Compare iterators with ==, !=, <
  • Pre-defined iterators: obj.begin(), obj.end()
  #include <iostream>
  #include <map>
  #include <vector>
  using namespace std;
  int main(){
    // Vector iterator.
    vector<double> x = {1, 2, 3};
    for (auto it = x.begin (); it != x.end (); ++it) {
      cout << * it << endl;
    }
    // Map Iterations
    map<int,string> m = {1,"hello"},{2,"world"}; // add {} cover the Rvalue. but somehow error in blog system, i just put that like this
    map<int,string>::iterator m_it = m.find(1);
    cout << m_it->first << ":" << m_it->second << endl;
    if (m.find (3) == m.end ()) {
      cout<<"Key 3 was not found \n"
    }
    return 0;
  }

Error handling with exceptions

  • We can “throw” an exception if there is an error
  • STL defines classes that represent exceptions. Base class: exception
  • An exception can be ‘‘caught’’ at any point of the program (try - catch) and even ‘‘thrown’’ further (throw)
  • The constructor of an exception receives a string error message as a parameter
  • This string can be called through a member function what()

throw exceptions

Runtime Error:

// if there is an error
if (badEvent ) {
  string msg = "specific error string";
  // throw error
  throw runtime_error (msg);
}

Logic Error: an error in logic of the user

throw logic_error (msg);

catch exceptions

  • If we expect an exception, we can ‘‘catch’’ it
  • Use try - catch to catch exceptions
    try {
    // some code that can throw exceptions z.B.
    x = someUnsafeFunction (a, b, c);
    }
    // we can catch multiple types of exceptions
    catch ( runtime_error &ex ) {
    cerr << "Runtime error: " << ex.what () << endl;
    } catch ( logic_error &ex ) {
    cerr << "Logic error: " << ex.what () << endl;
    } catch ( exception &ex ) {
    cerr << "exception: " << ex.what () << endl;
    } catch ( ... ) {
    cerr << "Error: unknown exception" << endl;
    }
    

Intuition

  • Only used for “exceptional behavior”
  • Often misused: e.g. wrong parameter should not lead to an exception
  • GOOGLE-STYLE Don’t use exceptions
  • http://www.cplusplus.com/reference/exception/

Program input parameters

  • Originate from the declaration of main function
  • Allow passing arguments to the binary
  • int main(int argc, char const * argv[]);
  • argc defines number of input parameters
  • argv is an array of string parameters
  • By default:
    • argc == 1
    • argv == “"
#include <iostream>
#include <string>
using namespace std;
int main(int argc , char const *argv []) {
  cout << "Got " << argc << " params\n";
  string program_name = argv[0];
  cout << "Program: " << program_name << endl;
  for (int i = 1; i < argc; ++i) {
    cout << "Param: " << argv[i] << endl;
  }
  return 0;
}

Using for type aliasing

  • Use word using to declare new types from existing and to create type aliases(别名)
  • Basic syntax: using NewType = OldType;
  • using is a versatile(通用的) word
  • When used outside of functions declares a new type alias
  • When used in function creates an alias of a type available in the current scope
  • http://en.cppreference.com/w/cpp/language/type_alias
#include <array>
#include <memory>

template <class T, int SIZE >
struct Image{
  // Can be used in classes.
  using Ptr = std :: unique_ptr <Image <T, SIZE >>;
  std::array<T, SIZE> data;
}
// Can be combined with "template".
template <int SIZE >
using Imagef = Image<float,SIZE>;
int main(){
  // Can be used in a function for type aliasing.
  using Image3f = Imagef<3>;
  auto image_ptr = Image3f::Ptr(new Image3f);
  return 0;
}

OpenCV

  • Popular library for Image Processing
  • We will be using version 2 of OpenCV
  • We will be using just a small part of it
  • #include <opencv2/opencv.hpp> to use all functionality available in OpenCV
  • Namespace cv::

Data types

  • OpenCV uses own types
  • OpenCV trusts you to pick the correct type
  • Names of types follow pattern
    • **CV_**
  • Example: RGB image is CV_8UC3: 8-bit unsigned char with 3 channels for RGB
  • Example: Grayscale image is CV_8UC1: single 8-bit unsigned char for intensity
  • Better to use DataType
  • Example: DataType::type == CV_8UC1

Basic Matrix Type

  • Every image is a cv::Mat, for ‘‘Matrix’
  • Mat image(rows, cols, DataType, Value);
  • **Mat_ image(rows, cols, Value);**
  • Initialize with zeros:
    cv:: Mat image = cv:: Mat :: zeros (10, 10, CV_8UC3);
    using Matf = cv::Mat_ <float >;
    Matf image_float = Matf :: zeros (10, 10);
    
  • Get type identifier with image.type();
  • Get size with image.rows, image.cols
  • I/O:
    • Read image with imread
    • Write image with imwrite
    • Show image with imshow
    • Detects I/O method from extension

cv::Mat is a shared pointer

  • It does not use std::shared_ptr but follows the same principle of reference counting
    #include <opencv2/opencv.hpp>
    #include <iostream>
    int main () {
    using Matf = cv::Mat_ <float >;
    Matf image = Matf :: zeros (10, 10);
    Matf image_no_copy = image; // Does not copy!
    image_no_copy.at<float>(5, 5) = 42.42f;
    std::cout << image.at<float>(5, 5) << std :: endl;
    Matf image_copy = image.clone(); // Copies image
    image_copy.at<float >(1, 1) = 42.42f;
    std :: cout<<image.at<float>(1, 1) << std :: endl;
    return 0;
    }
    
  • result
    c++ -std=c++11 -o copy copy.cpp \`pkg -config --libs --cflags opencv `
    

Imread

  • Read image from file
  • Mat imread(const string& file, int mode=1)
  • Different modes:
    • unchanged: CV_LOAD_IMAGE_UNCHANGED < 0
    • 1 channel: CV_LOAD_IMAGE_GRAYSCALE == 0
    • 3 channels: CV_LOAD_IMAGE_COLOR > 0
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
int main(){
  Mat i1 = imread("logo_opencv.png",CV_LOAD_IMAGE_GRAYSCALE );
  Mat_ <uint8_t > i2 = imread("logo_opencv.png", CV_LOAD_IMAGE_GRAYSCALE );
  std::cout << (i1.type () == i2.type ()) << std::endl;
  return 0;
}

imwrite

  • Write the image to file
  • Format is guessed from extension
  • bool imwrite(const string& file, const Mat& img);
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
int main () {
  cv:: Mat image = cv:: imread("logo_opencv.png", CV_LOAD_IMAGE_COLOR );
  cv:: imwrite("copy.jpg", image);
  return 0;
}

Write float images to * .exr files

  • When storing floating point images OpenCV expects the values to be in [0, 1] range
  • When storing arbitrary values the values might be cut off
  • Save to * .exr files to avoid this
  • These files will store and read values as is without losing precision
    #include <iostream>
    #include <opencv2/opencv.hpp>
    #include <string>
    int main(){
    using Matf = cv::Mat_ <float>;
    Matf image = Matf:: zeros(10, 10);
    image.at<float>(5, 5) = 42.42f;
    std::string f = "test.exr";
    cv:: imwrite(f, image);
    Matf copy = cv:: imread(f, CV_LOAD_IMAGE_UNCHANGED );
    std::cout << copy.at <float >(5, 5) << std :: endl;
    return 0;
    }
    

imshow

  • Display the image on screen
  • Needs a window to display the image
  • void imshow(const string& window_name, const Mat& mat)
    #include <opencv2/opencv.hpp>
    int main(){
    cv:: Mat image = cv:: imread("logo_opencv.png",CV_LOAD_IMAGE_COLOR );
    std :: string window_name = "Window name";
    // Create a window.
    cv::namedWindow(window_name , cv:: WINDOW_AUTOSIZE);
    cv:: imshow(window_name , image); // Show image.
    cv:: waitKey (); // Don't close window instantly.
    return 0;
    }
    
  • OpenCV vector type: cv::Vec<Type, SIZE>
  • Many typedefs available: Vec3f, Vec3b, etc.
  • Used for pixels in multidimensional images: mat.at(row, col);
  • 사진의 픽셀을 고르는데 사용된다.(single pixels)
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
int main(){
  Mat mat = Mat::zeros (10, 10, CV_8UC3);
  std:: cout << mat.at <Vec3b>(5, 5) << std::endl;
  Mat_ <Vec3f> matf3 = Mat_ <Vec3f>::zeros(10, 10);
  std::cout << matf3.at <Vec3f >(5, 5) << std :: endl;
  return 0;
}

Mixing up types is painful!

  • OpenCV trusts you to pick the type
  • This can cause errors
  • OpenCV interprets bytes stored in cv::Mat according to the type the user asks (similar to reinterpret_cast)
  • Make sure you are using correct types!
#include <opencv2/opencv.hpp>
int main () {
  cv::Mat image = cv::Mat::zeros (800 , 600, CV_8UC3);
  std::string window_name = "Window name"
  cv::namedWindow(window_name , cv::WINDOW_AUTOSIZE );
  cv::imshow(window_name , image);
  cv:: waitKey();
  for (int r = 0; r < image.rows; ++r) {
    for (int c = 0; c < image.cols; ++c) {
      // WARNING! WRONG TYPE USED!
      image.at <float >(r, c) = 1.0f;
    }
  }
  cv:: imshow(window_name , image);
  cv:: waitKey ();
  return 0;
}

SIFT Descriptors

  • SIFT: Scale Invariant Feature Transform
  • Popular features: illumination, rotation and translation invariant (to some degree)

SIFT Extraction With OpenCV

  • SiftFeatureDetector to detect the keypoints
  • SiftDescriptorExtractor to compute descriptors in keypoints
// Detect key points
SiftFeatureDetector detector;
vector <KeyPoint> keypoints ; // 픽셀들을 모아놓는다 키포인트
detector.detect(input, keypoints);
// Show the keypoints on the image.
Mat image_with_keypoints ;
drawKeypoints (input , keypoints , image_with_keypoints );
// Extract the SIFT descriptors.
SiftDescriptorExtractor extractor ;
extractor .compute(input , keypoints , descriptors );

FLANN in OpenCV

  • FLANN: Fast Library for Approximate Nearest Neighbors
  • build K-d tree, search for neighbors there
// Create a kdtree for searching the data.
cv:: flann :: KDTreeIndexParams index_params ;
cv:: flann :: Index kdtree(data , index_params );
...
// Search the nearest vector to some query
Mat nearest_vector_idx (1, k, DataType <int >:: type);
Mat nearest_vector_dist (1, k, DataType <float >:: type);
kdtree. knnSearch (query , nearest_vector_idx , nearest_vector_dist , k);

OpenCV 2 with CMake

  • Install OpenCV 2 in the system
    sudo add -apt - repository ppa:xqms/opencv -nonfree
    sudo apt update
    sudo apt install libopencv -dev libopencv -nonfree -dev
    
  • Find using find_package(OpenCV 2 REQUIRED)
    find_package(OpenCV 2 REQUIRED )
    
  • Include ${OpenCV_INCLUDE_DIRS}
  • Link against ${OpenCV_LIBS}
    add_library(some_lib some_lib_file .cpp)
    target_link_libraries(some_lib ${OpenCV_LIBS})
    add_executable( some_program some_file .cpp)
    target_link_libraries( some_program ${OpenCV_LIBS})
    

    Additional OpenCV information

  • Example project with additional information about using SIFT and FLANN can be found here:
    • https://gitlab.igg.uni-bonn.de/teaching/example_opencv
    • https://docs.opencv.org/2.4/modules/nonfree/doc/feature_detection.html

      Reference

      https://www.ipb.uni-bonn.de/teaching/modern-cpp/

Cpp Core Guidelines: https://github.com/isocpp/CppCoreGuidelines

Git guide: http://rogerdudler.github.io/git-guide/

C++ Tutorial: http://www.cplusplus.com/doc/tutorial/

Book: Code Complete 2 by Steve McConnell

Modern CMake Tutorial https://www.youtube.com/watch?v=eC9-iRN2b04

Compiler Explorer: https://godbolt.org/ Gdbgui: https://www.gdbgui.com/ CMake website: https://cmake.org/ Gdbgui tutorial: https://www.youtube.com/watch?v=em842geJhfk

Fluent C++: structs vs classes: https://google.github.io/styleguide/cppguide.html#Structs_vs._Classes

Comments