剖析 OpenCV (一) Point 类

OpenCV 的 type.hpp 头文件里包含了基本的数据类型,它们都是以类的形式定义的,例如 Complex,Point 等。

下面简单剖析 Point 的源代码,并借此学习 C++ 的一些基础知识。

1  template class Point_

1.1  模板类定义

/* 1) template<typename _Tp>
*     - specify that a template is being declared and a type argument _Tp will be used
*     - _Tp is used exactly like other type name */
template<typename _Tp> class Point_
{
public:

/* 2) define an alias for a type with the using or typedef syntax */
    typedef _Tp value_type;

/* 3) constructor
*     - a member with the same name as its class
*     - its declaration specifies an argument list but has no return type
*     - its job is to initialize an object of its class */
    Point_();
    Point_(_Tp _x, _Tp _y);
    Point_(const Point_& pt);
    Point_(const Size_<_Tp>& sz);
    Point_(const Vec<_Tp, 2>& v);

/* 4) assignment operator */
    Point_& operator = (const Point_& pt);

/* 5) conversion operator
*     - a special kind of member function that converts a value of a class type
*       to a value of some other type.
*     - its general form is: operator type() const; */
    //! conversion to another data type
    template<typename _Tp2> operator Point_<_Tp2>() const;
    //! conversion to the old-style C structures
    operator Vec<_Tp, 2>() const;

/* 6) member functions */
    //! dot product
    _Tp dot(const Point_& pt) const;
    //! dot product computed in double-precision arithmetics
    double ddot(const Point_& pt) const;
    //! cross-product
    double cross(const Point_& pt) const;
    //! checks whether the point is inside the specified rectangle
    bool inside(const Rect_<_Tp>& r) const;

/* 7) member data */
    _Tp x, y; //< the point coordinates
};

/* 8) typedef
*    - type aliases are defined for convenience */
typedef Point_<int> Point2i;
typedef Point_<float> Point2f;
typedef Point_<double> Point2d;
typedef Point2i Point;

1.2  构造函数实现

/* constructors implementation */
// 1) default
template<typename _Tp> inline
Point_<_Tp>::Point_()
    : x(0), y(0) {}

// 2) specify x and y
template<typename _Tp> inline
Point_<_Tp>::Point_(_Tp _x, _Tp _y)
    : x(_x), y(_y) {}

// 3) Point_
template<typename _Tp> inline
Point_<_Tp>::Point_(const Point_& pt)
    : x(pt.x), y(pt.y) {}

// 4) Size_<_Tp>
template<typename _Tp> inline
Point_<_Tp>::Point_(const Size_<_Tp>& sz)
    : x(sz.width), y(sz.height) {}

// 5) Vec<_Tp, 2>
template<typename _Tp> inline
Point_<_Tp>::Point_(const Vec<_Tp,2>& v)
    : x(v[0]), y(v[1]) {}

第四个和第五个构造函数,分别使用了 Size_<_Tp> 和 Vec<_Tp, 2>, 下面来看看它们各自的定义

template class Size_

template<typename _Tp> class Size_
{
public:
    typedef _Tp value_type;

    //! various constructors
    Size_();
    Size_(_Tp _width, _Tp _height);
    Size_(const Size_& sz);
    Size_(const Point_<_Tp>& pt);

    Size_& operator = (const Size_& sz);
    //! the area (width*height)
    _Tp area() const;

    //! conversion of another data type.
    template<typename _Tp2> operator Size_<_Tp2>() const;

    _Tp width, height; // the width and the height
};

typedef Size_<int> Size2i;
typedef Size_<float> Size2f;
typedef Size_<double> Size2d;
typedef Size2i Size;

template class Vec (in matx.hpp)

/* The Vec class is commonly used to describe pixel types of multi-channel arrays */
template<typename _Tp, int cn> class Vec : public Matx<_Tp, cn, 1>
{
public:
    typedef _Tp value_type;
    enum { depth    = Matx<_Tp, cn, 1>::depth,
           channels = cn,
           type     = CV_MAKETYPE(depth, channels)
         };

    //! default constructor
    Vec();

    Vec(_Tp v0); //!< 1-element vector constructor
    Vec(_Tp v0, _Tp v1); //!< 2-element vector constructor
    Vec(_Tp v0, _Tp v1, _Tp v2); //!< 3-element vector constructor
    Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3); //!< 4-element vector constructor
    Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4); //!< 5-element vector constructor
    Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5); //!< 6-element vector constructor
    Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6); //!< 7-element vector constructor
    Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7); //!< 8-element vector constructor
    Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8); //!< 9-element vector constructor
    Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9); //!< 10-element vector constructor
    Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9, _Tp v10, _Tp v11, _Tp v12, _Tp v13); //!< 14-element vector constructor
    explicit Vec(const _Tp* values);

    Vec(const Vec<_Tp, cn>& v);

    static Vec all(_Tp alpha);

    //! per-element multiplication
    Vec mul(const Vec<_Tp, cn>& v) const;

    //! conjugation (makes sense for complex numbers and quaternions)
    Vec conj() const;

    /*!
      cross product of the two 3D vectors.

      For other dimensionalities the exception is raised
    */
    Vec cross(const Vec& v) const;
    //! conversion to another data type
    template<typename T2> operator Vec<T2, cn>() const;

    /*! element access */
    const _Tp& operator [](int i) const;
    _Tp& operator[](int i);
    const _Tp& operator ()(int i) const;
    _Tp& operator ()(int i);

    Vec(const Matx<_Tp, cn, 1>& a, const Matx<_Tp, cn, 1>& b, Matx_AddOp);
    Vec(const Matx<_Tp, cn, 1>& a, const Matx<_Tp, cn, 1>& b, Matx_SubOp);
    template<typename _T2> Vec(const Matx<_Tp, cn, 1>& a, _T2 alpha, Matx_ScaleOp);
};

/* short aliases for Vec<T,n> */
typedef Vec<uchar, 2> Vec2b;
typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;

typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;

typedef Vec<ushort, 2> Vec2w;
typedef Vec<ushort, 3> Vec3w;
typedef Vec<ushort, 4> Vec4w;

typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;
typedef Vec<int, 6> Vec6i;
typedef Vec<int, 8> Vec8i;

typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;

typedef Vec<double, 2> Vec2d;
typedef Vec<double, 3> Vec3d;
typedef Vec<double, 4> Vec4d;
typedef Vec<double, 6> Vec6d;

显然 Vec 继承于 Matx<_Tp, cn, 1>, 于是追本溯源,看其 template class Matx 的定义

// Template class for small matrices whose type and size are known at compilation time
template<typename _Tp, int m, int n> class Matx
{
public:
    enum { depth    = DataType<_Tp>::depth,
           rows     = m,
           cols     = n,
           channels = rows*cols,
           type     = CV_MAKETYPE(depth, channels),
           shortdim = (m < n ? m : n)
         };

    typedef _Tp                           value_type;
    typedef Matx<_Tp, m, n>               mat_type;
    typedef Matx<_Tp, shortdim, 1> diag_type;

    //! default constructor
    Matx();

    Matx(_Tp v0); //!< 1x1 matrix
    Matx(_Tp v0, _Tp v1); //!< 1x2 or 2x1 matrix
    Matx(_Tp v0, _Tp v1, _Tp v2); //!< 1x3 or 3x1 matrix
    Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3); //!< 1x4, 2x2 or 4x1 matrix
    Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4); //!< 1x5 or 5x1 matrix
    Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5); //!< 1x6, 2x3, 3x2 or 6x1 matrix
    Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6); //!< 1x7 or 7x1 matrix
    Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7); //!< 1x8, 2x4, 4x2 or 8x1 matrix
    Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8); //!< 1x9, 3x3 or 9x1 matrix
    Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9); //!< 1x10, 2x5 or 5x2 or 10x1 matrix
    Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3,
         _Tp v4, _Tp v5, _Tp v6, _Tp v7,
         _Tp v8, _Tp v9, _Tp v10, _Tp v11); //!< 1x12, 2x6, 3x4, 4x3, 6x2 or 12x1 matrix
    Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3,
         _Tp v4, _Tp v5, _Tp v6, _Tp v7,
         _Tp v8, _Tp v9, _Tp v10, _Tp v11,
         _Tp v12, _Tp v13); //!< 1x14, 2x7, 7x2 or 14x1 matrix
    Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3,
         _Tp v4, _Tp v5, _Tp v6, _Tp v7,
         _Tp v8, _Tp v9, _Tp v10, _Tp v11,
         _Tp v12, _Tp v13, _Tp v14, _Tp v15); //!< 1x16, 4x4 or 16x1 matrix
    explicit Matx(const _Tp* vals); //!< initialize from a plain array

    static Matx all(_Tp alpha);
    static Matx zeros();
    static Matx ones();
    static Matx eye();
    static Matx diag(const diag_type& d);
    static Matx randu(_Tp a, _Tp b);
    static Matx randn(_Tp a, _Tp b);

    //! dot product computed with the default precision
    _Tp dot(const Matx<_Tp, m, n>& v) const;

    //! dot product computed in double-precision arithmetics
    double ddot(const Matx<_Tp, m, n>& v) const;

    //! conversion to another data type
    template<typename T2> operator Matx<T2, m, n>() const;

    //! change the matrix shape
    template<int m1, int n1> Matx<_Tp, m1, n1> reshape() const;

    //! extract part of the matrix
    template<int m1, int n1> Matx<_Tp, m1, n1> get_minor(int i, int j) const;

    //! extract the matrix row
    Matx<_Tp, 1, n> row(int i) const;

    //! extract the matrix column
    Matx<_Tp, m, 1> col(int i) const;

    //! extract the matrix diagonal
    diag_type diag() const;

    //! transpose the matrix
    Matx<_Tp, n, m> t() const;

    //! invert the matrix
    Matx<_Tp, n, m> inv(int method=DECOMP_LU, bool *p_is_ok = NULL) const;

    //! solve linear system
    template<int l> Matx<_Tp, n, l> solve(const Matx<_Tp, m, l>& rhs, int flags=DECOMP_LU) const;
    Vec<_Tp, n> solve(const Vec<_Tp, m>& rhs, int method) const;

    //! multiply two matrices element-wise
    Matx<_Tp, m, n> mul(const Matx<_Tp, m, n>& a) const;

    //! divide two matrices element-wise
    Matx<_Tp, m, n> div(const Matx<_Tp, m, n>& a) const;

    //! element access
    const _Tp& operator ()(int i, int j) const;
    _Tp& operator ()(int i, int j);

    //! 1D element access
    const _Tp& operator ()(int i) const;
    _Tp& operator ()(int i);

    Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_AddOp);
    Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_SubOp);
    template<typename _T2> Matx(const Matx<_Tp, m, n>& a, _T2 alpha, Matx_ScaleOp);
    Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_MulOp);
    Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_DivOp);
    template<int l> Matx(const Matx<_Tp, m, l>& a, const Matx<_Tp, l, n>& b, Matx_MatMulOp);
    Matx(const Matx<_Tp, n, m>& a, Matx_TOp);

    _Tp val[m*n]; //< matrix elements
};

typedef Matx<float, 1, 2> Matx12f;
typedef Matx<double, 1, 2> Matx12d;
typedef Matx<float, 1, 3> Matx13f;
typedef Matx<double, 1, 3> Matx13d;
typedef Matx<float, 1, 4> Matx14f;
typedef Matx<double, 1, 4> Matx14d;
typedef Matx<float, 1, 6> Matx16f;
typedef Matx<double, 1, 6> Matx16d;

typedef Matx<float, 2, 1> Matx21f;
typedef Matx<double, 2, 1> Matx21d;
typedef Matx<float, 3, 1> Matx31f;
typedef Matx<double, 3, 1> Matx31d;
typedef Matx<float, 4, 1> Matx41f;
typedef Matx<double, 4, 1> Matx41d;
typedef Matx<float, 6, 1> Matx61f;
typedef Matx<double, 6, 1> Matx61d;

typedef Matx<float, 2, 2> Matx22f;
typedef Matx<double, 2, 2> Matx22d;
typedef Matx<float, 2, 3> Matx23f;
typedef Matx<double, 2, 3> Matx23d;
typedef Matx<float, 3, 2> Matx32f;
typedef Matx<double, 3, 2> Matx32d;

typedef Matx<float, 3, 3> Matx33f;
typedef Matx<double, 3, 3> Matx33d;

typedef Matx<float, 3, 4> Matx34f;
typedef Matx<double, 3, 4> Matx34d;
typedef Matx<float, 4, 3> Matx43f;
typedef Matx<double, 4, 3> Matx43d;

typedef Matx<float, 4, 4> Matx44f;
typedef Matx<double, 4, 4> Matx44d;
typedef Matx<float, 6, 6> Matx66f;
typedef Matx<double, 6, 6> Matx66d;

再看看其中一个构造函数 Matx(_Tp v0, _Tp v1, _Tp v2) 的实现

template<typename _Tp, int m, int n> inline
Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2)
{
    CV_StaticAssert(channels >= 3, "Matx should have at least 3 elements.");
    val[0] = v0; val[1] = v1; val[2] = v2;
    for(int i = 3; i < channels; i++) val[i] = _Tp(0);
}

最后回到 Vec 构造函数 Vec(_Tp v0, _Tp v1) 的实现

template<typename _Tp, int cn> inline
Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1)
    : Matx<_Tp, cn, 1>(v0, v1) {}

至此,Size_<_Tp> 和 Vec<_Tp, 2> 也就清晰可见了

1.3  成员函数

回到模板类 Point_ 的成员函数,首先看看赋值运算符的实现

1)  赋值操作符

// 1) assignment operator
template<typename _Tp> inline
Point_<_Tp>& Point_<_Tp>::operator = (const Point_& pt)
{
    x = pt.x; y = pt.y;
    return *this;
}

注意其返回值为 *this,是实例 pt 自身的引用。

在 <Effective C++> 书中,有对此的解释,item 10 - have assignment operators return a reference to this*,详细的解释如下:

/* 1) chain of assignments */
// assignment returns a reference to its left-hand argument
int x, y, z;
x = y = z = 15;        // right-associative
x = (y = (z = 15)); // more clear way

/* 2) assignment operators for classes */
class Widget{
public:
    ...
    // return type is a reference to the current class
    Widget& operator=(const Widget& rhs)
    {
        ...
        return  *this; //return the left-hand object
    }
    ...
};

// apply to all assignment operators
class Widget{
public:
    ...
    // +=, -=, *=
    Widget& operator+= (const Widget& rhs)
    {
        ...
        return *this;
    }
    // it applies even if the parameter type is unconventional
    Widget& operator=(int rhs)
    {
        ...
        return *this;
    }
    ...
};

// this convention is followed by all built-in types and
// by all the types in STL(e.g., string, vector, complex)

2)  转换操作符

// 2) conversion operator
//    - Point_<_Tp2>()
//    - Vec<_Tp, 2>()
template<typename _Tp> template<typename _Tp2> inline
Point_<_Tp>::operator Point_<_Tp2>() const
{
    return Point_<_Tp2>(saturate_cast<_Tp2>(x), saturate_cast<_Tp2>(y));
}

template<typename _Tp> inline
Point_<_Tp>::operator Vec<_Tp, 2>() const
{
    return Vec<_Tp, 2>(x, y);
}

saturate_cast

3)  成员函数 dot 等

// 3) member functions: dot, ddot, cross, inside
template<typename _Tp> inline
_Tp Point_<_Tp>::dot(const Point_& pt) const
{
    return saturate_cast<_Tp>(x*pt.x + y*pt.y);
}

template<typename _Tp> inline
double Point_<_Tp>::ddot(const Point_& pt) const
{
    return (double)x*pt.x + (double)y*pt.y;
}

template<typename _Tp> inline
double Point_<_Tp>::cross(const Point_& pt) const
{
    return (double)x*pt.y - (double)y*pt.x;
}

template<typename _Tp> inline bool
Point_<_Tp>::inside( const Rect_<_Tp>& r ) const
{
    return r.contains(*this);
}

成员函数 inside 里面调用了 Rect_ 的 contains 成员函数。于是继续剖析,寻找到模板类 Rect_ 的定义和其成员函数 contains 的实现如下:

// Template class for 2D rectangles
template<typename _Tp> class Rect_
{
public:
    typedef _Tp value_type;

    //! various constructors
    Rect_();
    Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height);
    Rect_(const Rect_& r);
    Rect_(const Point_<_Tp>& org, const Size_<_Tp>& sz);
    Rect_(const Point_<_Tp>& pt1, const Point_<_Tp>& pt2);

    Rect_& operator = ( const Rect_& r );
    //! the top-left corner
    Point_<_Tp> tl() const;
    //! the bottom-right corner
    Point_<_Tp> br() const;

    //! size (width, height) of the rectangle
    Size_<_Tp> size() const;
    //! area (width*height) of the rectangle
    _Tp area() const;

    //! conversion to another data type
    template<typename _Tp2> operator Rect_<_Tp2>() const;

    //! checks whether the rectangle contains the point
    bool contains(const Point_<_Tp>& pt) const;

    _Tp x, y, width, height; //< the top-left corner, as well as width and height of the rectangle
};

// type aliases defined for convenience
typedef Rect_<int> Rect2i;
typedef Rect_<float> Rect2f;
typedef Rect_<double> Rect2d;
typedef Rect2i Rect;

// the implementation of member function contains
template<typename _Tp> inline
bool Rect_<_Tp>::contains(const Point_<_Tp>& pt) const
{
    return x <= pt.x && pt.x < x + width && y <= pt.y && pt.y < y + height;
}

附: 成员函数内的参数是 pass-by-reference-to-const 的形式,该形式在 <Effective C++> 书中也有详细的解释

item 20 - Prefer pass-by-reference-to-const to pass-by-value

/**************************************************
* 1. more efficient
***************************************************/
// class Person and its subclass Student
class Person{
public:
    Person();    // parameters omitted for simplicity
    virtual ~Person();
    ...
private:
    std::string name;
    std::string address;
};

class Student: public Person{
public:
    Student();    // parameters again omitted
    ~Student();
    ...
private:
    std::string schoolName;
    std::string schoolAddress;
};

// 1) pass-by-value
bool validateStudent(Student s);

// inefficient: need to construct a Student object, two string objects,
// a Person object, two string objects and also destruct those objects
Student plato;
bool platoOK = validateStudent(plato);

// 2) pass-by-reference-to-const
// efficient: no constructors or destructors are called
bool validateStudent(const Student& s);

/*************************************************
* 2. avoid slicing
**************************************************/
class Window{
public:
    ...
    std::string name() const;        // return name of window
    virtual void display() const;    // draw window and contents
};

class WindowWithScrollBars{
public:
    ...
    virtual void display() const;
};

// 1) pass-by-value is incorrect
void printNameAndDisplay(Window w)
{
    std::cout << w.name();
    w.display();
}
// WindowWithScrollBars object will be sliced off
WindowWithScrollBars  wwsb;
printNameAndDisplay(wwsb);

// 2) pass-by-reference-to-const is correct
void printNameAndDisplay(const Window& w)
{
    std::cout << w.name();
    w.display();
}

1.4  运算符重载(非成员函数)

这几个操作符的重载都比较简单,主要涉及不同数据类型间的运算,同时也需注意返回值与函数参数 const 类型的关系

// 1) +=, -=, *=, /=

// +=
template<typename _Tp> static inline
Point_<_Tp>& operator += (Point_<_Tp>& a, const Point_<_Tp>& b)
{
    a.x += b.x;
    a.y += b.y;
    return a;
}

// -=
template<typename _Tp> static inline
Point_<_Tp>& operator -= (Point_<_Tp>& a, const Point_<_Tp>& b)
{
    a.x -= b.x;
    a.y -= b.y;
    return a;
}

// *=
template<typename _Tp> static inline
Point_<_Tp>& operator *= (Point_<_Tp>& a, int b)
{
    a.x = saturate_cast<_Tp>(a.x * b);
    a.y = saturate_cast<_Tp>(a.y * b);
    return a;
}

template<typename _Tp> static inline
Point_<_Tp>& operator *= (Point_<_Tp>& a, float b)
{
    a.x = saturate_cast<_Tp>(a.x * b);
    a.y = saturate_cast<_Tp>(a.y * b);
    return a;
}

template<typename _Tp> static inline
Point_<_Tp>& operator *= (Point_<_Tp>& a, double b)
{
    a.x = saturate_cast<_Tp>(a.x * b);
    a.y = saturate_cast<_Tp>(a.y * b);
    return a;
}

// /=
template<typename _Tp> static inline
Point_<_Tp>& operator /= (Point_<_Tp>& a, int b)
{
    a.x = saturate_cast<_Tp>(a.x / b);
    a.y = saturate_cast<_Tp>(a.y / b);
    return a;
}

template<typename _Tp> static inline
Point_<_Tp>& operator /= (Point_<_Tp>& a, float b)
{
    a.x = saturate_cast<_Tp>(a.x / b);
    a.y = saturate_cast<_Tp>(a.y / b);
    return a;
}

template<typename _Tp> static inline
Point_<_Tp>& operator /= (Point_<_Tp>& a, double b)
{
    a.x = saturate_cast<_Tp>(a.x / b);
    a.y = saturate_cast<_Tp>(a.y / b);
    return a;
}

// 2) norm
template<typename _Tp> static inline
double norm(const Point_<_Tp>& pt)
{
    return std::sqrt((double)pt.x*pt.x + (double)pt.y*pt.y);
}

// 3) ==, !=
template<typename _Tp> static inline
bool operator == (const Point_<_Tp>& a, const Point_<_Tp>& b)
{
    return a.x == b.x && a.y == b.y;
}

template<typename _Tp> static inline
bool operator != (const Point_<_Tp>& a, const Point_<_Tp>& b)
{
    return a.x != b.x || a.y != b.y;
}

// 4) +, binary -, unary -
// +
template<typename _Tp> static inline
Point_<_Tp> operator + (const Point_<_Tp>& a, const Point_<_Tp>& b)
{
    return Point_<_Tp>( saturate_cast<_Tp>(a.x + b.x), saturate_cast<_Tp>(a.y + b.y) );
}

// binary -
template<typename _Tp> static inline
Point_<_Tp> operator - (const Point_<_Tp>& a, const Point_<_Tp>& b)
{
    return Point_<_Tp>( saturate_cast<_Tp>(a.x - b.x), saturate_cast<_Tp>(a.y - b.y) );
}

// unary -
template<typename _Tp> static inline
Point_<_Tp> operator - (const Point_<_Tp>& a)
{
    return Point_<_Tp>( saturate_cast<_Tp>(-a.x), saturate_cast<_Tp>(-a.y) );
}

// 5) mixed mode *
template<typename _Tp> static inline
Point_<_Tp> operator * (const Point_<_Tp>& a, int b)
{
    return Point_<_Tp>( saturate_cast<_Tp>(a.x*b), saturate_cast<_Tp>(a.y*b) );
}

template<typename _Tp> static inline
Point_<_Tp> operator * (int a, const Point_<_Tp>& b)
{
    return Point_<_Tp>( saturate_cast<_Tp>(b.x*a), saturate_cast<_Tp>(b.y*a) );
}

template<typename _Tp> static inline
Point_<_Tp> operator * (const Point_<_Tp>& a, float b)
{
    return Point_<_Tp>( saturate_cast<_Tp>(a.x*b), saturate_cast<_Tp>(a.y*b) );
}

template<typename _Tp> static inline
Point_<_Tp> operator * (float a, const Point_<_Tp>& b)
{
    return Point_<_Tp>( saturate_cast<_Tp>(b.x*a), saturate_cast<_Tp>(b.y*a) );
}

template<typename _Tp> static inline
Point_<_Tp> operator * (const Point_<_Tp>& a, double b)
{
    return Point_<_Tp>( saturate_cast<_Tp>(a.x*b), saturate_cast<_Tp>(a.y*b) );
}

template<typename _Tp> static inline
Point_<_Tp> operator * (double a, const Point_<_Tp>& b)
{
    return Point_<_Tp>( saturate_cast<_Tp>(b.x*a), saturate_cast<_Tp>(b.y*a) );
}

template<typename _Tp> static inline
Point_<_Tp> operator * (const Matx<_Tp, 2, 2>& a, const Point_<_Tp>& b)
{
    Matx<_Tp, 2, 1> tmp = a * Vec<_Tp,2>(b.x, b.y);
    return Point_<_Tp>(tmp.val[0], tmp.val[1]);
}

template<typename _Tp> static inline
Point3_<_Tp> operator * (const Matx<_Tp, 3, 3>& a, const Point_<_Tp>& b)
{
    Matx<_Tp, 3, 1> tmp = a * Vec<_Tp,3>(b.x, b.y, 1);
    return Point3_<_Tp>(tmp.val[0], tmp.val[1], tmp.val[2]);
}

// 6) mixed mode /
template<typename _Tp> static inline
Point_<_Tp> operator / (const Point_<_Tp>& a, int b)
{
    Point_<_Tp> tmp(a);
    tmp /= b;
    return tmp;
}

template<typename _Tp> static inline
Point_<_Tp> operator / (const Point_<_Tp>& a, float b)
{
    Point_<_Tp> tmp(a);
    tmp /= b;
    return tmp;
}

template<typename _Tp> static inline
Point_<_Tp> operator / (const Point_<_Tp>& a, double b)
{
    Point_<_Tp> tmp(a);
    tmp /= b;
    return tmp;
}

1.5  使用实例

每一次类模板的实例化,都单独构成一个独立的类。例如, Point_<int> 和 Point_<float> 便是两个完全独立的类

// type aliases are defined for convenience
typedef Point_<int> Point2i;
typedef Point_<float> Point2f;
typedef Point_<double> Point2d;
typedef Point2i Point;

// Example
Point2f a(0.3f, 0.f), b(0.f, 0.4f);
Point pt = (a + b)*10.f;
cout << pt.x << ", " << pt.y << endl;

时间: 2024-10-08 13:43:03

剖析 OpenCV (一) Point 类的相关文章

利用opencv的FileStorage类实现XML/YAML文件的读写

FileStorage是opencv2.0以后专门用来读写XML/YAML文件的类,标准的C++实现.利用好XML文件可以极大地方便我们对中间数据的处理. 官方文档: (1) http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/core/file_input_output_with_xml_yml/file_input_output_with_xml_yml.html#fileinputoutputxmlyaml (2) htt

OpenCV SVM多类分类问题编程示例

2014年4月,北京--TechExcel喜签网目信息技术(上海)有限公司(以下简称"网目信息"),采用TechExcel公司CustomerWise产品助力其建立全球用户统一门户,统一各分公司的服务标准和服务流程,为用户提供全球标准化的服务. ICONZ-Webvisions公司(网目信息)总部设在新加坡,拥有20多年的IT运维及外包服务经验历史,是唯一以亚洲为中心,为亚洲.澳大利亚和全球市场提供"云"和托管服务的供应商.ICONZ-Webvisions可以利用其

OpenCv学习笔记(一)----OpenCv中Mat类源码的详细解读(2)

(一)像素存储的方法 1--本节我们讲解如何存储像素,存储像素值,需要指定: 1--颜色空间 2--数据类型 2--其中,颜色空间是指针对一个给定的颜色,如何组合颜色以其编码. 3--最简单的颜色空间是----灰度级空间----只需要处理:黑色和白色,对它们进行组合便可以产生不同程度的灰 色(256灰度级) 4--对于彩色方式---则有更多种类的颜色空间,但不论那种方式,都是把颜色分成:三个或者四个---基元素,通过 组合基元素,就可以产生所有的颜色 1--RGB颜色空间是最常用的一种颜色空间,

OpenCV中CascadeClassifier类实现多尺度检测源码解析

级联分类器检测类CascadeClassifier,在2.4.5版本中使用Adaboost的方法+LBP.HOG.HAAR进行目标检测,加载的是使用traincascade进行训练的分类器 class CV_EXPORTS_W CascadeClassifier { public: CV_WRAP CascadeClassifier(); // 无参数构造函数,new自动调用该函数分配初试内存 CV_WRAP CascadeClassifier( const string& filename )

Opencv中Rect类

转载: Rect_类有些意思,成员变量x.y.width.height,分别为左上角点的坐标和矩形的宽和高.常用的成员函数有Size()返回值为一个Size,area()返回矩形的面积,contains(Point)用来判断点是否在矩形内,inside(Rect)函数判断矩形是否在该矩形内,tl()返回左上角点坐标,br()返回右下角点坐标.值得注意的是,如果想求两个矩形的交集,并集,可以用如下格式 [cpp] view plain copy Rect rect = rect1 & rect2;

WorldWind源码剖析系列:挂件类Widgets

WorldWindow用户定制控件类中所包含的的挂件类Widgets控件主要有如下图所示的派生类.它们的类图如下所示. 鉴于挂件类Widgets及其派生类,相对简单,基本上都是些利用DirectX3D进行绘图和处理图标纹理影像等的操作,此处不再对各个类的主要的字段.属性和方法进行描述了.感兴趣的读者可以直接阅读源码.建议阅读源码之前读者应具备一定的DirectX3D开发基础. 挂件PictureBox类被WavingFlags.TimeController等插件引擎子类所引用. 挂件Button

WorldWind源码剖析系列:相机类[未完]

PluginSDK中的相机类CameraBase是三维计算机图形学中的概念.观察者在三维场景中漫游时,通过眼睛看到的场景和相机拍摄过程非常一致.实际上,Direct3D和OpenGL都是先通过对现实世界中的场景先进行世界变换,再通过设置观察矩阵以在场景中安置一个虚拟相机,构建一个视景体来裁剪场景的可见区域,然后在通过投影变换(平行投影或透视投影),获取三维场景的“像”,最后再通过视口变换,将场景的“像”光栅化输出到二维显示屏幕上.如下图所示. 在三维地形系统中,通常定义一个虚拟相机来实现对三维场

WorldWind源码剖析系列:四元数类

PluginSDK中的Quaternion4d类可能是感觉Microsoft.DirectX. Quaternion类不太实用或不够用,自己有重新写的. 四元数是英国数学家哈密顿(W.R.Hamilton)在1843年发现的,由于矩阵论的不断丰富和不断完善,人们更乐意采用矩阵来解决实际工程中的问题,这导致四元数在相当长的时间里没有被人们重视,更没有得到实际的应用.随着计算机图形学的发展,人们发现利用四元数可以很好地处理解决旋转运算等问题,这一理论又开始被人们重视,并在许多领域逐渐得到应用. 四元

volley 框架剖析(三) Request类精解

Request是所有网络请求的基类,它实现了Comparable接口,前面提到RequestQueue可按照优先级队进行排序,这里的Comparable就是为优先级排序作准备. 接下来,我们对Request中比较重要或有趣的成员或方法进行一一解释. Request中包括一个对支持的Http方法的定义.这里使用的内部接口而不是枚举来实现的. public interface Method { int DEPRECATED_GET_OR_POST = -1; int GET = 0; int POS