Learning OpenCV3

Learning OpenCV3

一、Overview

1.计算机视觉概念

类比于人在道路两侧停放的车中寻找特定的车,图像信号通过眼睛传输到大脑,大脑有注意力识别系统:通过任务依赖的方式,检查图像重要区域,抑制其他区域。利用人生活多年来形成的交叉关联的感觉识别重要信息,再由大脑的反馈回路返回到传感器(眼睛肌肉),进一步去获取需要的图像。

###2.遇到的问题

用2D去描绘3D世界的误差,数据的失真和噪声破坏。具体如下:

  • 上下文信息

    • 例子:

      机器人在房子里捡起订书机,首先机器人可能会用到以下事实:办公桌是办公室内的对象,而订书机是最大可能在办公桌上找到,这是一个隐式的大小位置参考;且机器人需要忽略相同大小形状的小木盒、以及墙画上的订书机图片。

      相反,对于图像检索任务,机器人查询的数据库里都是真实的订书机,很可能就只拍摄了正常尺寸的订书机;在拍摄这些图片时,也隐藏这很多无意的隐式信息。

    • 解决:

      使用机器学习进行显示建模,对隐藏变量(大小、形状、重力方向等)用与他们值相关的标签训练集中进行校正。或者使用其他传感器测量隐藏的偏差分量。

  • 噪音

    • 例子:

      仅通过几个点比较无法检查出物体边缘,有些像素点可能在生成、传输的途中遭到破坏,不能准确识别。

    • 解决

      统计局部区域可较准确的进行边缘检测

      通过建立直接从数据中学到的显式模型来描述,如人们已经很了解镜头畸变,只需建立一个多项式模型参数,通过调整参数从而更正失真。

二、opencv入门示例

简单的入门示例,这里有一个正好前段时间涉及到的边缘检测算法(二值化->区分前后景),效果如下:

​ 阈值为1、10,可以看到边缘检测很不清晰,把很多rgb值高的点也包含进来

阈值为1,10

​ 阈值为10、100,可以看到颜色较重的区域也能区分出来

阈值10,100

​ 阈值为100、500,这样小鱼的边缘就很清晰了

阈值100,500

三、OpenCV数据类型

从组织的角度来看,数据分为以下三类:

  • c++原语(int, float等):简单向量和矩阵、简单几何概念(点、矩阵、尺寸等)
  • 辅助对象:抽象概念,如垃圾回收指针、切片对象、终止条件等
  • 大数组类型

1.基本类型

  • Point

    基于模板结构实现的,可以是任何类型的点

    • 分类:

      • 二维、三维、四维点
      • cv::Point2i,最后一个字母表示构造点的原语类型。(b:无符号字符,s:短整型,i:32位整数,f:32位浮点数,d:64位浮点数)
    • 操作:

      | 运行方式 | 例子 |
      | :——————: | :———————–: |
      | 默认构造函数 | cv::Point2i p; |
      | 复制构造函数 | cv::Point3f p2(p1) |
      | 值构造函数 | cv::Point3d p(x0, x1, x2) |
      | 转换为固定向量类 | (cv::Vec3f) |
      | 访问成员 | p.x; |
      | 点乘 | float x = p1.dot(p2); |
      | 双精度点乘 | double x = p1.ddot(p2); |
      | 叉乘 | p1.cross(p2); |
      | 查询点p是否在矩形r里 | p.inside(r); |

    • cv::Scalar类

      四维点类,所有成员时双精度浮点数

      | 运行方式 | 示例 |
      | :—————: | :————————————————-: |
      | 默认构造函数 | cv::Scalar s; |
      | 复制构造函数 | cv::Scalar s2(s1); |
      | 值构造函数 | cv::Scalar s(x0, x1, x2, x3); |
      | 逐元素乘法 | s1.mul(s2); |
      | (四元数)共轭 | s.conj(); // (return cv::Scalar(s0, -s1, -s2, -s3)) |
      | (四元数)real test | s.isReal(); // (returns true if s1==s2==s3==0) |

      该类操作强制转换到固定向量类,与point类不同,cv::Scalar直接从固定向量类模板的实例化中继承

  • size

    size类类似于point类,类型有Size、Size2i、Size2f,唯一区别就是命名区别(x、y与width、height)

    | 运行方式 | 示例 |
    | :———-: | :——————: |
    | 默认构造函数 | cv::Size sz; |
    | 复制构造函数 | cv::Size sz2(sz1); |
    | 值构造函数 | cv::Size2f sz(w, h); |
    | 访问成员 | sz.width; sz.height; |
    | 计算面积 | sz.area(); |

  • Rect

    矩形类包含point类的成员x,y(代表矩形左上角坐标),和宽高类(矩形的大小),但矩形不继承点类

    | 运作方式 | 示例 |
    | :————————–: | :——————————-: |
    | r1、r2矩形的交点 | cv::Rect r3 = r1&r2; r1 &= r2; |
    | 包含矩形r1、r2的最小面积矩形 | cv::Rect r3 = r1 ! r2; r1 |= r2; |
    | 用x平移矩形r | cv::Rect rx = r + x; r += x; |
    | 将矩形r按大小s增大 | cv::Rect rs = r + s; r += s; |
    | 比较矩形是否相等 | bool eq = (r1 == r2); |
    | 比较矩形是否不 | bool eq = (r1 != r2); |

  • RotatedRect

    c++ Opencv接口中为数不多的没有模板的类之一,相反,这是一个容器,保存了一个cv::Point2f(中心),一个cv::Size2f(大小)和一个额外的浮点数(角度),角度表示矩形围绕中心的旋转

    | 运行方式 | 示例 |
    | :————————: | :—————————-: |
    | 默认构造函数 | cv::RotatedRect rr(); |
    | 复制构造函数 | cv::RotatedRect rr2(rr1); |
    | 从两个角度构建 | cv::RotatedRect(p1, p2); |
    | 值构造函数(点,大小,角度) | cv::RotatedRect(p, sz, theta); |
    | 访问成员 | rr.center; rr.size; rr.angle; |
    | 返回角列表 | rr.points(pts[4]); |

  • fixed matrix

    固定矩阵类是指在编译时位数已知的矩阵,其数据的所有内存都分配在堆栈上,意味着可以快速地分配和清理。在c++ Opencv接口中,固定矩阵类实际上是一个模板,是许多其他基本类的核心(固定向量类派自固定矩阵类,其他类则派生自固定向量类)。

    | 运作方式 | 示例 |
    | :——————————: | :———————————————————-: |
    | 默认构造函数 | cv::Matx33f m33f; cv::MAtx43d m43d; |
    | 复制构造函数 | cv::Matx22d m22d(n22d); |
    | 值构造函数 | cv::Matx21f m(x0, x1); |
    | 相同元素的矩阵 | m33f = cv::Matx33f::all(x); |
    | 零矩阵 | m23d = cv::Matx23d::zeros(); |
    | 一个矩阵 | m16f = cv::Matx16f::ones(); |
    | 创建单位矩阵 | m33f = cv::Matx33f::eye(); |
    | 创建一个可容纳另一个对角线的矩阵 | m31f = cv::Matx33f:diag(); //31的矩阵 |
    | 创建一个具有均匀分布项的矩阵 | m33f = cv::Matxf::randu(min, max); |
    | 创建一个具有正态分布条项的矩阵 | m33f = cv::Matx33f::nrandn(mean, virance); |
    | 访问成员 | m(i, j); m(i); |
    | 矩阵代数 | m1 = m0; m0
    m1; m0 + m1; m0 -m1; |
    | 单例代数 | m a; a m; m / a; |
    | 比较 | m1 == m2; m1 != m2; |
    | 点积 | m1.dot(m2); //m的精度 |
    | 点积 | m1.ddot(m2); //双精度 |
    | 重塑矩阵 | m91f = m33f.reshape<9, 1="">(); |
    | 类型转换 | m44f = (Matx44f)m44d; |
    | 提取子矩阵 | m44f.get_minor<2,2>(i,j); |
    | 提取第i行 | m14f = m44f.row(i); |
    | 提取第j列 | m14f = m44f.col(j); |
    | 提取矩阵对角线 | m41f = m44f.diag(); |
    | 计算转置 | n44f = m44f.t(); |
    | 求逆矩阵 | n44f = m44f.inv(method); //默认方法为cv::DECOMP_LU |
    | 解线性方程组 | m31f = m33f.solve(rhs31f, method); m32f = m33f.solve2(rhs32f, method); // 模板表单,默认方法为DECOMP_LU |
    | 逐元素乘法 | m1.mul(m2); |

    注意,许多固定矩阵函数相对于类都是静态的(作为类的成员而不是特定的成员直接访问他们),如构造3*3的矩阵,cv::Mat33f::eye()。

  • fixed vector

    固定向量类是从固定矩阵类派生的,可以看作cv::Matx<>的便捷功能。

    | 运作方式 | 示例 |
    | :———-: | :——————-: |
    | 默认构造函数 | Vec2s v2s; Vec6f v6s; |
    | 复制构造函数 | Vec3s u3f(v3f); |
    | 值构造函数 | Vec2f v2f(x0, x1); |
    | 访问成员 | v4f[i]; v3w(j); |
    | 向量叉乘 | v3f.cross(u3f); |

    主要便利是使用单顺序访问元素

  • complex number

    复数类与STL复合体关联的类最实质的区别在于:在STL类中,通过real()和image()访问实数和虚数;而在OpenCV类中,直接访问公共成员变量re和im

2.辅助对象

  • TermCriteria

    停止条件

  • Range

    范围

  • Ptr模板和垃圾回收

    c++中智能指针,该指针是我们能创建对对象的引用,所有引用被计数,当引用超出范围时,智能指针的引用计数将减少,一旦所有引用(指针的实例)减到0,实例对象将被自动清除(已分配)。

    与c++中智能指针类似,为想要“包装”的类对象实例化一个指针模板(cv::Ptr p = makePtr()),模板对象的构造函数指向对象的指针。此操作后,智能指针p可像普通指针一样传递和使用(即,支持运算符operator*()和operator->()),可创建其他相同类型的对象,而不需要向它们传递新对象的指针。如:Ptr q,并将p的值赋给q。这里只有一个实际的p和q指向的cv::Mat33f对象,p和q都知道他们各自为1.若p消失(超出范围),则q知道它时唯一剩下的对源实矩阵的引用;若q消失,且其析构函数被调用,q就知道它时唯一引用的指针,q会取消分配原始矩阵。

  • Exception类和异常处理

  • DataType

    当OpenCV库函数需要传递特殊概念的数据类型时,可通过创建cv::DataType<>类型的对象来实现。传递的实际对象时这个模板的实例对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // OpenCV中DataType的定义
    template<> class DataType<float>
    {
    public:
    typedef float value_type;
    typedef value_type work_type;
    typedef value_type channel_type;
    typedef value_type vec_type;

    enum {
    generix_type = 0,
    depth = DataDepth<channel_type>::value,
    channels = 1,
    fmt = DataDepth<channel_type>::fmt,
    type = CV_MAKETYPE(depth, channels)
    };
    };
  • InputArray和OutputArray

    输入、输出对象

3.实用函数

除了上述专用原始数据类型,OpenCV还提供一些有效处理数学运算和其他运算的专用功能,该使用功能包含数学工具、测试、错误生成、内存和线程处理、优化等

函数 功能
cv::aliginPtr() 将指针对齐到给定的字节数
cv::aliginSize() 将缓冲区大小与给定的字节数对齐
cv :: allocate() 分配c样式的对象数组
cvCeil() 将浮点数向上取整
cv::cubeRoot() 计算立方根
cv::CV_Assert() 若给定条件不成立,则引发异常
CV_Error() 宏以构建cv :: Exception (从固定字符串)并将其抛出
CVErrpr() 宏来构建cv :: Exception (从格式化的字符串)并抛出它
cv::deallocate() 取消分配c样式的对象数组
cv::error() 提示错误并引发异常
cv::fastAtan2() 计算向量的二维角度
cv::fastFree() 取消分配内存缓冲区
cv::fastMalloc() 分配对齐的内存缓冲区
cvFloor() 将浮点数向下取整
cv::format() 使用类似sprintf的格式,创建STL字符串
cv::getCPUTickCount() 从内部cpu计时器获得滴答计数
cv::getNumThreads() 计算OpenCV当前使用的线程
cv::getOptimalDFTSize() 为计划传递给cv::DFT()的数组计算最佳大小
cv::getThreadNum() 获取当前线程的索引
cv::getTickCount() 从系统获取滴答计数
cv::getTickFrequency() 每秒获取数字/刻度
cvIsInf() 检查浮点数x是否为无穷大
cvIsNan() 检查浮点数是否为“非数字“
cvRound() 将浮点数舍入到最近的整数
cv::setNumThreads() 设置OpenCV使用的线程数
cv::setUseOptimized() 启/禁用代码优化
cv::useOptimized() 指示代码优化启用的状态

具有旧版界面的功能,为c定义的。

4.模板结构

四、图像和大数组类型

1.动态和可变存储

  • cv::Mat类:N维密集数组

    cv::mat类可用于任何数目维度的阵列,数据储存在数组中,可以认为是一个n维“光栅扫描”的模拟。每个矩阵包含一个表示数组内容的flags元素、一个表示维度的dims元素、行列元素、一个表示位置的数据指针、以及一个引用计数器 .可把数组理解为.

  • 生成一个数组

    实例化一个cv::Mat类,并通过其成员函数create()来分配数据,其所需参数:行数、列数、类型、以及数组代表二维对象的配置。有效数组类型指定元素的基本类型和通道数量,所有这些类型都在库头文件中定义,其形式为:CV_{8U,16S,16U,32S,32F,64F}C{1,2,3}.如:CV_32FC3代表一个32位浮点三通道数组.

    1
    2
    3
    4
    5
    6
    7
    8
    //示例

    cv::Mat m;
    m.create(3, 10, CV_32FC3); // 为一个3行10列3通道的32位浮点数组分配数据
    m.setTo(cv::Scalar(1.0f, 0.0f, 1.0f)); // 三通道值分别为1.0, 0.0, 1.0

    // 等效上三行
    cv::Mat m( 3, 10, CV_32FC3, cv::Scalar( 1.0f, 0.0f, 1.0f ) );
    • 构造函数

      | 函数 | 功能 |
      | :———————————————————-: | :—————————————: |
      | cv::Mat(); | 默认构造函数 |
      | cv::Mat(int rows, int cols, int type); | 二维数组 |
      | cv::Mat(int rows, int cols, int type, const Scalar& s); | 带初始值的二维数组 |
      | cv::Mat(int rows, int cols, int type, void data, size_t step=AUTO_STEP); | 具有类型的二维数组,预先存在的数据 |
      | cv::Mat(cv::Size sz, int type); | 二维数组,sz大小 |
      | cv::Mat(cv::Size sz, int type, const Scalar& s); | 带初始值的二维数组,sz大小 |
      | cv::Mat(cv::Size sz, int type, void data, size_t step=AUTO_STEP); | 具有类型的二维数组,预先存在的数据,sz大小 |
      | cv::Mat(int ndims, const int
      sizes, int type); | 多维数组 |
      | cv::Mat(int ndims, const int sizes, int type, const Scalar& s); | |
      | cv::Mat(int ndims, const int
      sizes, int type, const Scalar& s, size_t step=AUTO_STEP); | |

  • 复制构造函数

    | 函数 | 功能 |
    | :———————————————————-: | :———————————————–: |
    | cv::Mat(const Mat& mat); | 复制构造函数 |
    | cv::Mat(const Mat& mat, const cv::Range& rows, const cv::Range& cols); | 复制构造函数,仅能复制行和列 |
    | cv::Mat(const Mat& mat, const cv::Rect& roi); | 复制构造函数,仅能复制感兴趣区域(roi)的行和列 |
    | cv::Mat(const Mat& mat, const cv::Range* ranges); | 通用区域复制构造函数,复制一块范维数组 |
    | cv::Mat(const cv::MatExpr& expr); | 复制用其他矩阵的代数表达式的结果初始化m的构造函数 |

  • 模板构造函数

    | 函数 | 功能 |
    | :———————————————————-: | :—————————————————–: |
    | cv::Mat(const cv::vec& vec, bool copyData = true); | 从同一类型的cv::Vec中构造一个类型为T、大小为n的一维数组 |
    | cv::Mat(const cv::Matx& vec, bool copyData = true); | 从同一类型的cv::Matx中构造一个尺寸为m×n的T型二维数组 |
    | cv::Mat(const std::vector& vec, bool copyData = true); | 从包含相同类型元素的STL向量构造T类型的一维数组 |

  • 静态函数

    | 函数 | 功能 |
    | :——————————–: | :——————————————————: |
    | scv::Mat::zeros(rows, cols, type); | 创建rows行cols列的cv::Mat,初始全为0,类型为type |
    | scv::Mat::ones(rows, cols, type); | 创建rows行cols列的cv::Mat,初始全为1,类型为type |
    | scv::Mat::eye(rows, cols, type); | 创建rows行cols列的cv::Mat,初始为identity矩阵,类型为type |

  • 分别访问数组元素

    • 几种访问函数

      | 函数 | 功能 |
      | :——————-: | :——————————–: |
      | M.at(i); | 整数数组M中第i个元素 |
      | M.at(i, j); | 浮点数组M中(i,j)位置元素 |
      | M.at(pt); | 整数矩阵M中(pt.x, pt.y)位置的元素 |
      | M.at(i, j, k); | 三维浮点数组M中(i, j, k)位置的元素 |
      | M.at(idx); | uchar数组M中idx[]位置的元素 |

      还可通过怕c样式的指针访问二维数组

      对于矩阵,有两种方法获取其数据指针:

      • 成员函数ptr<>()
      • M.at<>()
  • N元数组迭代器:NAryMatlterator

    一次遍历多个数组,返回数组块,称为planes,常用于多数组计算(多点代表面)

  • 按快访问数组元素

未完待续!

-------------本文结束感谢您的阅读-------------
显示 Gitment 评论