精华内容
下载资源
问答
  • opencv存储MAT矩阵中的数据

    千次阅读 2016-03-25 15:30:08
    在视觉处理过程中,往往需要保存中间数据。这些数据的数据类型往往并不是整数。 OpenCV的C++接口中,用于保存图像的imwrite只能保存整数数据,且需作为图像格式。当需要保存浮点数据或XML/YML文件时,...矩阵存储

    在视觉处理过程中,往往需要保存中间数据。这些数据的数据类型往往并不是整数。

    OpenCV的C++接口中,用于保存图像的imwrite只能保存整数数据,且需作为图像格式。当需要保存浮点数据或XML/YML文件时,OpenCV的C语言接口提供了cvSave函数,但这一函数在C++接口中已经被删除。取而代之的是FileStorage类。


    具体使用方法参照这个例子:

    矩阵存储

    1. Mat mat = Mat::eye(Size(12,12), CV_8UC1);
    2. FileStorage fs(".\\vocabulary.xml", FileStorage::WRITE);
    3. fs<<"vocabulary"<<mat;
    4. fs.release();

    在另一处,需要加载这个矩阵数据。代码如下:

    1. FileStorage fs(".\\vocabulary.xml", FileStorage::READ);
    2. Mat mat_vocabulary;
    3. fs["vocabulary"] >> mat_vocabulary;

    在存储数据时,fs<<"vocabulary"<<mat将mat矩阵保存在了声明fs对象时制定的xml文件的vocabulary标签下,也可换成其它标签。可以多个<<符号连续使用,程序将自动将引号内容理解为标签名,不带引号的理解为数据变量或者常量。

    在读取数据时,[ ]中的内容为指定的标签,并将数据读入>>的变量中。

    展开全文
  • opencv2矩阵操作

    千次阅读 2015-11-14 15:46:50
    综述: OpenCV有针对矩阵操作的C语言函数. 许多其他方法提供了更加方便的C++接口,其效率与OpenCV一样. ...矩阵按行存储,每行有4字节的校整. //由于opencv矩阵式一位数组或者一位指针,所以
    • 文章转载自新浪博客:http://blog.sina.com.cn/s/blog_7908e1290101i97z.html   
    • 综述:

      • OpenCV有针对矩阵操作的C语言函数. 许多其他方法提供了更加方便的C++接口,其效率与OpenCV一样.
      • OpenCV将向量作为1维矩阵处理.
      • 矩阵按行存储,每行有4字节的校整.
      • //由于opencv的矩阵式一位数组或者一位指针,所以我们只能利用opencv的函数对矩阵元素进行操作(当然这样也是最安全的做法,- -!太不习惯了)
    • 分配矩阵空间:
      CvMat* cvCreateMat(int rows, int cols, int type);
      
       
      
      type: 矩阵元素类型. 格式为CV_(S|U|F)C. 例如: CV_8UC1 表示8位无符号单通道矩阵, CV_32SC2表示32位有符号双通道矩阵.
      
      例程: CvMat* M = cvCreateMat(4,4,CV_32FC1)//这个函数用来初始化Mat,不初始化虽然是会报warrying,但是运行时会报错,由于Mat和IplImage的类型不太一样,所以做两者之间转换的时候想改变数据类型很难(大概可以先转换矩阵类型,再转换数据类型,这就麻烦了,还没找到好方法),还需要注意这个分配空间并不管理数据的初始化,最好利用cvSet对数据进行初始化。 
      




    • 释放矩阵空间:
      CvMat* M = cvCreateMat(4,4,CV_32FC1); cvReleaseMat(&M);//这个别忘了用就是了 
      
    • 复制矩阵:
      CvMat* M1 = cvCreateMat(4,4,CV_32FC1);
      
      CvMat* M2;
      
      M2=cvCloneMat(M1);
      
    • 初始化矩阵:
      double a[] = { 1, 2, 3, 4,
      
      5, 6, 7, 8, 9, 10, 11, 12 };
      
       
      
      CvMat Ma=cvMat(3, 4, CV_64FC1, a);
      
      另一种方法:
      
      CvMat Ma; cvInitMatHeader(&Ma, 3, 4, CV_64FC1, a);
      
    • 初始化矩阵为单位阵:
     CvMat* M = cvCreateMat(4,4,CV_32FC1); cvSetIdentity(M); // 这里似乎有问题。
    
     //这个函数可以初始化对角线上的元素,这个是单位阵,不是所有元素都赋1,这个函数需要第二个函数,虽然有默认值,但是这个变量和我之前的理解有点不对,也没有实验,贴个原型吧: 
    
     void cvSetIdentity( CvArr* mat, CvScalar value=cvRealScalar(1) );这里的cvRealScalar(1)是个强制类型转换。 
    
    • 存取矩阵元素

    • 假设需要存取一个2维浮点矩阵的第(i,j)个元素.
    • 间接存取矩阵元素:
    • cvmSet(M,i,j,2.0); // Set M(i,j) t = cvmGet(M,i,j); // Get M(i,j)
      
    • 直接存取,假设使用4-字节校正:
      CvMat* M = cvCreateMat(4,4,CV_32FC1); int n = M->cols; float *data = M->data.fl; data[i*n+j] = 3.0;
      
    • 直接存取,校正字节任意:
      CvMat* M = cvCreateMat(4,4,CV_32FC1);
      
      int step = M->step/sizeof(float);
      
      float *data = M->data.fl;//这个也是Mat和IplImage的区别,Mat的data是union类型,里面是五个不同类型的指针,根据你存储的类型选择相同类型的指针;而IplImage的imagedata就直接存储数据了。
      
       
      
      (data+i*step)[j] = 3.0;
      
    • 直接存取一个初始化的矩阵元素:
      double a[16]; CvMat Ma = cvMat(3, 4, CV_64FC1, a); a[i*4+j] = 2.0; // Ma(i,j)=2.0;
      
    • 矩阵/向量操作

    • 矩阵-矩阵操作:
      CvMat *Ma, *Mb, *Mc; cvAdd(Ma, Mb, Mc); // Ma+Mb -> Mc cvSub(Ma, Mb, Mc); // Ma-Mb -> Mc cvMatMul(Ma, Mb, Mc); // Ma*Mb -> Mc//从前几天看到的一些技巧,我感觉这个几个函数不是很好,没有返回值,不能嵌套调用,想完成三个矩阵的相乘,还需要临时变量,比如恢复SVD的时候。 
      
    • 按元素的矩阵操作:
      CvMat *Ma, *Mb, *Mc; cvMul(Ma, Mb, Mc); // Ma.*Mb -> Mc cvDiv(Ma, Mb, Mc); // Ma./Mb -> Mc cvAddS(Ma, cvScalar(-10.0), Mc); // Ma.-10 -> Mc
      
    • 向量乘积:
      double va[] = {1, 2, 3}; double vb[] = {0, 0, 1}; double vc[3];
      
      CvMat Va=cvMat(3, 1, CV_64FC1, va); CvMat Vb=cvMat(3, 1, CV_64FC1, vb); CvMat Vc=cvMat(3, 1, CV_64FC1, vc);
      
      double res=cvDotProduct(&Va,&Vb); // 点乘: Va . Vb -> res cvCrossProduct(&Va, &Vb, &Vc); // 向量积: Va x Vb -> Vc end{verbatim}
      

      注意 Va, Vb, Vc 在向量积中向量元素个数须相同.

    • 单矩阵操作:
      CvMat *Ma, *Mb; cvTranspose(Ma, Mb); // transpose(Ma) -> Mb (不能对自身进行转置) CvScalar t = cvTrace(Ma); // trace(Ma) -> t.val[0] double d = cvDet(Ma); // det(Ma) -> d cvInvert(Ma, Mb); // inv(Ma) -> Mb
      
    • 非齐次线性系统求解:
      CvMat* A = cvCreateMat(3,3,CV_32FC1); CvMat* x = cvCreateMat(3,1,CV_32FC1); CvMat* b = cvCreateMat(3,1,CV_32FC1); cvSolve(&A, &b, &x); // solve (Ax=b) for x
      
    • 特征值分析(针对对称矩阵):
      CvMat* A = cvCreateMat(3,3,CV_32FC1); CvMat* E = cvCreateMat(3,3,CV_32FC1); CvMat* l = cvCreateMat(3,1,CV_32FC1); cvEigenVV(&A, &E, &l); // l = A的特征值 (降序排列)
      
      // E = 对应的特征向量 (每行)
      
    • 奇异值分解SVD:
      CvMat* A = cvCreateMat(3,3,CV_32FC1); CvMat* U = cvCreateMat(3,3,CV_32FC1); CvMat* D = cvCreateMat(3,3,CV_32FC1); CvMat* V = cvCreateMat(3,3,CV_32FC1); cvSVD(A, D, U, V, CV_SVD_U_T|CV_SVD_V_T); // A = U D V^T
      

      标号使得 U 和 V 返回时被转置(若没有转置标号,则有问题不成功!!!).//这个函数解释比较少,首先这个函数不能接受IplImage类型的输入变量,其次必须是浮点型,原文中这个问题,我没有遇到,我使用时最后一个参数用的是默认值0。

    //

         自己遇到的问题:

        1,首先是初始化的问题,opencv提供了初始化Mat和初始化MatHead两种方法,文档中介绍都不分配内存存储数据,但实际上cvCreateMat这个函数是分配空间的,类推IplImage也是这样。不初始化会在运行时报错。

        2,为了调用SVD函数,我要用到很多Mat类型的数据,而且要从IplImage转化为Mat,这里可以选择两个函数,一个是cvGetImage,一个是cvConvert。cvGetImage会将输入数据的文件头一并复制,不符合我的要求,我的输入是IPL_DEPTH_8U ,要转化的矩阵是CV_32FC1,第二个函数不会复制文件头。另外要注意,Mat类型中的type包含很多信息,不光有矩阵的数据类型,不要尝试在矩阵复制后,更改type达到上面的目的。更改type会导致release不成功。

        3,opencv不支持不同大小的矩阵相加,我又初始化一个载体大小的图像,将水印的奇异值矩阵复制过去,这是我程序为数不多的几次自己编写的程序块,注意更改单个矩阵值的函数是cvmSet和cvmGet。不要自己找啊。

        4,cvCreateMat函数并不会对数据进行初始化,最好继续用cvSet对数据进行初始化。


    目标

         我们有多种方法可以获得从现实世界的数字图像:数码相机、扫描仪、计算机体层摄影或磁共振成像就是其中的几种。在每种情况下我们(人类)看到了什么是图像。但是,转换图像到我们的数字设备时我们的记录是图像的每个点的数值。

     

          例如在上图中你可以看到车的镜子只是一个包含所有强度值的像素点矩阵。现在,我们如何获取和存储像素值可能根据最适合我们的需要而变化,最终可能减少计算机世界内的所有图像数值矩阵和一些其他的信息的描述基质本身。OpenCV 是一个计算机视觉库,其主要的工作是处理和操作,进一步了解这些信息。因此,你需要学习和开始熟悉它的第一件事是理解OpenCV 是如何存储和处理图像。

    Mat

          OpenCV 自 2001 年出现以来。在那些日子里库是围绕C接口构建的。在那些日子里,他们使用名为IplImage C 的结构在内存中存储图像。这是您将在大多数较旧的教程和教材中看到的那个。使用这个结构的问题是将 C 语言的所有负面效果都摆到了桌面上。最大的问题是手动管理。它是建立在用户来负责处理内存分配和解除分配的假设之上的。当程序规模较小时,这是没有问题的,一旦代码基开始变得越来越大它将会越来越挣扎着处理所有这一切而不是着眼于实际解决自己的开发目标。

          幸运的是 c + + 出现了,并引入了类的概念,使得为用户开辟另一条路成为可能:

          自动内存管理 (或多或少)。好消息是,c + +,如果完全兼容 C 所以进行更改时没有兼容性问题产生。因此, OpenCV其2.0 版本引入一个新的c + + 接口,通过利用这些优点将为你的工作提供新的方法。某种程度上,在其中您不需要拨弄内存管理让你的代码简洁 (写得更少,实现的更多)。C + + 接口的唯一主要缺点在于,目前许多嵌入式的开发系统支持仅 C.因此,除非您的目标是这一平台,否则就没有理由再使用旧的方法(除非你是个受虐狂程序员和喜欢自讨苦吃)。

          你需要知道的关于Mat的第一件事是你不再需要手动分配其大小并且当你不需要它的时候你不再需要手动释放它。虽然这样做仍然是可能的,大多数 OpenCV 函数将手动分配其输出数据。还有一个额外的好处是如果传递一个已存在Mat对象,它已经为矩阵分配所需的空间,这段空间将被重用。也就是说我们在任何时候只使用与我们执行任务时所必须多的内存一样多的内存。

           Mat本质上是由两个数据部分组成的类: (包含信息有矩阵的大小,用于存储的方法,矩阵存储的地址等) 的矩阵头和一个指针,指向包含了像素值的矩阵(可根据选择用于存储的方法采用任何维度存储数据)。矩阵头部的大小是恒定的。然而,矩阵本身的大小因图像的不同而不同,通常是较大的数量级。因此,当你在您的程序中传递图像并在有些时候创建图像副本您需要花费很大的代价生成图像矩阵本身,而不是图像的头部。OpenCV 是图像处理库,它包含大量的图像处理函数。若要解决的计算挑战,最终大部分时间你会使用库中的多个函数。由于这一原因图像传给库中的函数是一种常见的做法。我们不应忘记我们正在谈论往往是计算量相当大的图像处理算法。我们想要做的最后一件事是通过制作不必要的可能很大的图像的拷贝进一步降低您的程序的速度。

          为了解决这一问题 OpenCV 使用引用计数系统。其思想是Mat的每个对象具有其自己的头,但可能他们通过让他们矩阵指针指向同一地址的两个实例之间共享该矩阵。此外,拷贝运算符将只能复制矩阵头部,也还将复制指针到大型矩阵,但不是矩阵本身。

     

    1. Mat A, C; //仅创建了头部
    2. A = imread(argv[1], CV_LOAD_IMAGE_COLOR); //在此我们知道使用的方法(分配矩阵)
    3. Mat B(A); //使用拷贝构造函数
    4. C = A; //赋值运算符
    复制代码

          上文中的所有对象,以相同的单个数据矩阵的结束点。他们头不同,但是使用的其中任何一个对矩阵进行任何修改,也将影响所有其他的。在实践中的不同对象只是提供相同的底层数据不同的访问方法,然而,它们的头部是不同的。真正有趣的部分是您可以创建仅指向完整数据的一小部分的头。例如,要在图像中创建兴趣区域 ( ROI) 您只需创建一个新头设置新边界:

     

    1. <p style="margin-top: 5px; margin-bottom: 5px; "><font size="2">Mat D (A, Rect(10, 10, 100, 100) );<em> // 用矩形界定</em></font></p><p style="margin-top: 5px; margin-bottom: 5px; "><font size="2">Mat E = A(Range:all(), Range(1,3)); <em>// 用行和列来界定</em></font></p>
    复制代码

    现在,你可能会问是否矩阵的本身可以属于多个Mat对象在不再需要时负责清理数据。简短的回答是:最后一个使用它的对象。这对于使用引用计数的机制,每当有人复制Mat对象的头,矩阵的计数器被增加。每当一个头被清除,此计数器被下调。当该计数器变为零,矩阵也就被释放了。因为有时会仍然也要复制矩阵的本身,存在着 clone() 或 copyTo() 函数。

     

    1. <p style="margin-top: 5px; margin-bottom: 5px; "><font size="2">Mat F = A.clone();</font></p><p style="margin-top: 5px; margin-bottom: 5px; "><font size="2">Mat G;</font></p><p style="margin-top: 5px; margin-bottom: 5px; "><font size="2">A.copyTo(G);</font></p>
    复制代码

    现在 modifyingForGwill 不会影响由 theMatheader 指出的矩阵。你要记得从所有的是:

               • 输出图像分配 OpenCV 功能是自动 (除非另行指定,否则)。

               • 用c + + OpenCV的接口就无需考虑内存释放。

               • 赋值运算符和复制构造函数 (构造函数)只复制头。

               • 使用clone () 或copyTo () 函数将复制的图像的基础矩阵。

    存储方法

          这是关于你是如何存储的像素值。您可以选择的颜色空间和使用的数据类型。色彩空间是指我们如何结合为了代码指定的颜色的颜色分量。最简单的是灰色的规模。在这里我们所掌握的颜色是黑色和白色。组合的这些让我们能创造很多的灰度级。

         对于彩色的方法,我们有很多方法可供选择。不过,每一就是将他们拆解成三个或四个基本组成部分,这些部分就会组合给所有其他的方法。最受欢迎的这一个 RGB,主要是因为这也是我们的眼睛如何建立中我们的眼睛的颜色。其基准的颜色是红、 绿、 蓝。编写代码的一种颜色的透明度有时第四个元素: 添加 alpha (A)。但是,它们很多颜色系统每个具有自身的优势:

               • RGB 是最常见的是我们的眼睛使用类似的事情,我们显示系统还撰写使用这些颜色。

               · 单纯疱疹和合肥分解颜色到他们的色相、 饱和度和亮度值/组件,这是我们来描述颜色更自然的方式。您使用,例如可驳回的最后一个组件,使你不那么明智的输入图像的光照条件的算法。

               • YCrCb 使用流行的 JPEG 图像格式。

               • CIE L *b*a 是均匀颜色空间,它是非常方便的如果您需要测量给定的颜色,以另一种颜色的距离。

          现在,每个建筑构件都自己有效的域。这会导致使用的数据类型。我们如何存储组件的定义只是如何精细的控制,我们已于其域。最小的数据类型可能是 char 类型,这意味着一个字节或 8 位。这可能是有符号(值-127 到 + 127)或无符号(以便可以存储从 0 到 255 之间的值)。虽然这三个组件的情况下已经给 16 万可能的颜色来表示 (如 RGB 的情况下) 我们可能通过使用浮点数 (4 字节 = 32 位) 或double(8 字节 = 64 位) 数据类型的每个组件获得甚至更精细的控制。然而,请记住增加组件的大小也会增加在内存中的整张图片的大小。

    显式创建Mat对象

          Load, Modify and Save an Image 教程中,你已经可以看到如何使用readWriteImageVideo: 'imwrite() <imwrite>' 函数将一个矩阵写到一个图像文件中。然而,出于调试目的显示的实际值就方便得多。您可以实现此通过Mat的 <<运算符。不过,请注意这仅适用于二维矩阵。

         虽然Mat是一个伟大的图像容器类,它也是一般矩阵类。因此,利用Mat创建和操作多维矩阵是可能的。您可以通过多种方式创建Mat的对象:

    • Mat()构造函数

    Mat M(2,2, CV_8UC3, Scalar(0,0,255));

     

    cout << "M = " << endl << " " << M << endl << endl;

          对于二维的和多通道的图像,我们首先定义它们的大小:按行和列计数。

         然后我们需要指定的数据类型,用于存储元素和每个矩阵点通道的数量。为此,我们根据以下的约定可以作出多个定义:

    CV_ [每一项的位数] [有符号或无符号] [类型前缀] C [通道数]

    例如,CV_8UC3 意味着我们使用那些长的 8 位无符号的 char 类型和每个像素都有三个项目的这三个通道的形成。这是预定义的四个通道数字。Scalar 是四个元素短向量。指定此和可以初始化所有矩阵点与自定义的值。但是如果你需要更多您可以创建与上部宏和频道号码放在括号中,您可以看到下面的类型。

          使用 C\C++ 数组和通过构造函数来初始化

     

    1. <p style="margin-top: 5px; margin-bottom: 5px; "><font size="2">int sz[3] = {2,2,2};</font></p><p style="margin-top: 5px; margin-bottom: 5px; "><font size="2">Mat L(3,sz,CV_8UC(1),Scalar::all(0));</font></p>
    复制代码

     

    上例为我们展示了如何创建一个二维以上的矩阵。首先指定其维度数,然后传入一个包含了尺寸每个维度信息的指针,其他都保持不变。

    •为一个已经存在的IplImage创建一个头:

    IplImage* img = cvLoadImage("greatwave.png", 1);

    Mat mtx(img); // 转换 IplImage*-> Mat

     

    • Create()函数:

    M.create(4,4, CV_8UC(2));

    cout << "M = "<< endl << " " << M << endl << endl;

     

    你不能通过这个构造来初始化矩阵中的数值。它只会在新的居住尺寸与旧的矩阵尺寸不合时重新分配矩阵的数据空间。

    • MATLAB风格的初始化函数:zeros(), ones(),

    :eyes().指定使用的尺寸和数据类型

     

    1. <p style="margin-top: 5px; margin-bottom: 5px; "><font size="2">Mat E = Mat::eye(4, 4, CV_64F);</font></p><p style="margin-top: 5px; margin-bottom: 5px; "><font size="2">cout << "E = " << endl << " " << E << endl << endl;</font></p><p style="margin-top: 5px; margin-bottom: 5px; "><font size="2">Mat O = Mat::ones(2, 2, CV_32F);</font></p><p style="margin-top: 5px; margin-bottom: 5px; "><font size="2">cout << "O = " << endl << " " << O << endl << endl;</font></p><p style="margin-top: 5px; margin-bottom: 5px; "><font size="2">Mat Z = Mat::zeros(3,3, CV_8UC1);</font></p><p style="margin-top: 5px; margin-bottom: 5px; "><font size="2">cout << "Z = " << endl << " " << Z << endl << endl;</font></p>
    复制代码

    •对于小的矩阵来说你可以使用逗号隔开的初始化函数:

    Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);

    cout << "C = " << endl << " " << C << endl << endl;

    •为一个已有的Mat对象创建一个新的头然后clone()或者copyTo()这个头.

    Mat RowClone = C.row(1).clone();

    cout << "RowClone = " << endl << " " << RowClone << endl << endl;

     

    打印格式

    注意:你可以通过用randu()函数产生的随机值来填充矩阵。你需要给定一个上限和下限来确保随机值在你期望的范围内:

    Mat R = Mat(3, 2, CV_8UC3);

    randu(R, Scalar::all(0), Scalar::all(255));

    在上一个例子中你可以看到默认的格式选项。尽管如此,OpenCV允许你在符合以下规则的同时格式化你的输出:

    • 默认

    cout << "R (default) = " << endl << R << endl << endl;

     

    • Python

    cout << "R (python) = " << endl << format(R,"python") << endl << endl;

     

    • Comma separated values (CSV)

    cout << "R (csv) = " << endl << format(R,"csv" ) << endl << endl;

     

    • Numpy

    cout << "R (numpy) = " << endl << format(R,"numpy" ) << endl << endl;

     

    • C

    cout << "R (c) = " << endl << format(R,"C" ) << endl << endl;

     

    打印出其它常见数据项

    OpenCV 通过<<操作符也为其他常用OpenCV数据结构提供打印输出的支持,如:

    • 2D 点

    Point2f P(5, 1);

    cout << "Point (2D) = " << P << endl << endl;

     

    • 3D 点

    Point3f P3f(2, 6, 7);

    cout << "Point (3D) = " << P3f << endl << endl;

     

    • std::vector通过 cv::Mat

    vector<float> v;

    v.push_back( (float)CV_PI); v.push_back(2); v.push_back(3.01f);

    cout << "Vector of floats via Mat = " << Mat(v) << endl << endl;

     

    •点的std::vector

    vector<Point2f> vPoints(20);

    for (size_t E = 0; E < vPoints.size(); ++E)

    vPoints[E] = Point2f((float)(E*5), (float)(E % 7));

    cout << "A vector of 2D Points = " << vPoints << endl << endl;

     

    这里大多数的例程都是在一个小控制台程序里运行。你可以在这里下载或是在cpp示例文件夹下找到。

    你可以在YouTube.上找到一个快速的实例演示

    Mat::~Mat

    Mat的析构函数。

    C++: Mat::~Mat()

    析构函数调用Mat::release()。

    Mat::operator =

    提供矩阵赋值操作。

    C++: Mat& Mat::operator=(const Mat& m)

    C++: Mat& Mat::operator=(const MatExpr_Base& expr)

    C++: Mat& Mat::operator=(const Scalar& s)

    参数:

    m – 被赋值的右侧的矩阵。 矩阵的赋值是一个复杂度为O(1) 的操作。 这就意味着没有数据段复制并且有数量的递增两矩阵将使用同一引用计数器。在给矩阵赋新数据之前先由Mat::release()释放引用。

    expr –被赋值的矩阵表达式对象。 作为第一种赋值方式的逆操作第二种形式可以被重新用到具有适当大小和尺寸的已分配空间的矩阵上以适应表达式的结果。矩阵表达式扩展得到的实函数将自动处理这个分配过程。例如:

    C=A+B 扩展成add(A, B, C) , andadd() 要当心C重新分配数据的操作。.

    s – 标量赋值给每一个矩阵元,矩阵的大小和类型将不会改变。有现成的赋值运算符。由于他们各不相同请阅读运算符参数说明。

    Mat::operator MatExpr

    提供一种Mat-to-MatExpr转换运算符

    C++: Mat::operator MatExpr_<Mat, Mat>() const

    转换运算符不能显示调用而是由矩阵表达式引擎(Matrix Expression engine)内部调用The cast operator should not be called explicitly. It is used internally by the Matrix Expressions engine.

    Mat::row

    创建一个指定行数的矩阵头。.

    C++: Mat Mat::row(int i) const

    参数:

    i – 一个0基的行索引.

    该方法创建一个具有指定了行数的新矩阵头的矩阵并返回它。这是一个复杂度为O(1) 的操作,无须考虑矩阵的尺寸。新矩阵和原矩阵共享一份基础数据。这是一个典型基本矩阵处理操作的例子, axpy是LU和许多其它算法都使用的一个函数

    inline void matrix_axpy(Mat& A, int i, int j, double alpha)

    {

    A.row(i) += A.row(j)*alpha;

    }

    Note:在当前实现中,下面的代码不会无法按预期的效果工作:

    Mat A ;

    ...

    A.row(i) = A.row(j) ;/ /不起作用

    发生这种情况是因为 A.row(i) 形成临时矩阵头进一步分配给另一个矩阵头。请记住,每个操作复杂度为O(1),即没有复制任何数据。因此,如果你预期第 j行被复制到第 i行,那么上述赋值不成立。要做到这一点,应该把这种简单的赋值转换到表达式中或使用 Mat::copyTo() 方法:

    Mat A ;

    ...

    / / 可行,但看上去有点目的不明确。

    A.row(i) = A.row(j) + 0;

    / / 这是有点儿长,但这是推荐的方法。

    A.row(j).copyTo(A.row(i)) ;

    Mat::col

    创建一个具有指定了矩阵头中列数这个参数的矩阵

    C++: Mat Mat::col(int j) const

    参数:

    j –一个0基(从0开始)的列索引

    该方法创建一个具有指定了矩阵头中列数这个参数的新矩阵并作为函数返回值。这是一种复杂度为O(1)的操作,不用考虑矩阵的尺寸大小。新矩阵和原始矩阵共享一份基础数据。参看Mat::row()说明信息。

    Mat::rowRange

    为指定的行span创建一个新的矩阵头。

    C++: Mat Mat::rowRange(int startrow, int endrow) const

    C++: Mat Mat::rowRange(const Range& r) const

    参数:

    startrow – 一个包容性的0基(从0开始)的行span起始索引.。

    endrow – 一个0基的独占性的行span.终止索引。

    r – Range 结构包含着起始和终止的索引值。该方法给矩阵指定的行span创建了新的头。 与Mat::row() 和 Mat::col()相类似这是一个复杂度为O(1)的操作。

    Mat::colRange

    为指定的行span创建一个矩阵头。

    C++: Mat Mat::colRange(int startcol, int endcol) const

    C++: Mat Mat::colRange(const Range& r) const

    参数:

    startcol – 一个包容性的0基(从0开始)的span列起始索引。

    endcol –一个0基的独占性的列span.终止索引。

    r –Range 结构包含着起始和终止的索引值。该方法给矩阵指定的列span创建了新的头。 与Mat::row() 和 Mat::col()相类似这是一个复杂度为O(1)的操作。

    Mat::diag

    提取或创建矩阵对角线。

    C++: Mat Mat::diag(int d) const

    C++: static Mat Mat::diag(const Mat& matD)

    参数:

    d – 对角线的索引值,可以是以下的值:

    – d=0 是主对角线

    – d>0表示下半部的对角线。例如:d=1对角线是紧挨着住对角线并位于矩阵下方。

    – d<0表示来自矩阵上半部的对角线。例如:d= 1表示对角线被设置在对角线的上方并紧挨着。

    matD – 单列用于形成矩阵对角线的列。

    该方法为指定的矩阵创建一个新的头。然后新矩阵被分割为单独的列矩阵。类似于Mat::row() 和Mat::col() ,它是复杂度为O(1)操作。

    Mat::clone

    创建一个数组及其基础数据的完整副本。

    C++: Mat Mat::clone() const

    该方法创建了一个完整的数组副本。原始的step[]不会被考虑在内的。因此数组的副本是一占用total()*elemSize()字节的连续阵列。

    Mat::copyTo

    把矩阵复制到另一个矩阵中。

    C++: void Mat::copyTo(OutputArray m) const

    C++: void Mat::copyTo(OutputArray m, InputArray mask) const

    参数:

    m – 目标矩阵。如果它的尺寸和类型不正确,在操作之前会重新分配。

    mask – 操作掩码。它的非零元素表示矩阵中某个要被复制。

    该方法把矩阵的复制到另一个新的矩阵中在复制之前该方法会调用

    m.create(this->size(), this->type);

    因此,目标矩阵会在必要的情况下重新分配

    尽管m.copyTo(m) works flawlessly,该函数并不处理源矩阵和目标矩阵之间有重叠的部分的情况。当操作掩码指定以及上述的Mat::create重新分配矩阵,新分配的矩阵在数据复制到里面之前全都被初始化为0。

    Mat::convertTo

    在缩放或不缩放的情况下转换为另一种数据类型。

    C++:

    void Mat::convertTo(OutputArray m,int rtype,double alpha=1,double beta=0)const

    参数:

    m – 目标矩阵。如果它的尺寸和类型不正确,在操作之前会重新分配。

    rtype – 要求是目标矩阵的类型,或者在当前通道数与源矩阵通道数相同的情况下的depth。如果rtype 为负,目标矩阵与源矩阵类型相同。

    beta – 可选的delta加到缩放值中去。

    该方法将源像素值转化为目标类型saturate_cast<> 要放在最后以避免溢出

    m( x;y) = saturate_cast < rType > ( α*( *this)( x;y) +β)

    Mat::assignTo

    提供了一个convertTo的功能形式。

    C++: void Mat::assignTo(Mat& m, int type=-1 ) const

    Parameters

    m – 目标阵列。

    type – 要求是目标阵列depth或-1(如果阵列的类型和源矩阵类型相同)

    这是一个 internally 使用的由 Matrix Expressions引擎调用的方法。

    Mat::setTo

    将阵列中所有的或部分的元素设置为指定的值。

    C++: Mat& Mat::setTo(const Scalar& s, InputArray mask=noArray())

    参数:

    s – 把标量赋给阵列并转化到阵列的实际类型。

    mask – 与 *this尺寸相同的操作掩码。这是Mat::operator=(const Scalar& s)运算符的一个高级变量。

    Mat::reshape

    在无需复制数据的前提下改变2D矩阵的形状和通道数或其中之一。

    C++: Mat Mat::reshape(int cn, int rows=0) const

    参数:

    cn – 新的通道数。若cn=0,那么通道数就保持不变。

    rows –新的行数。 若rows = 0, 那么行数保持不变。

    该方法为*this元素创建新的矩阵头。这新的矩阵头尺寸和通道数或其中之一发生改变,在以下的情况任意组合都是有可能的:

    ü 新的矩阵没有新增或减少元素。通常,rows*cols*channels()在转换过程中保持一致。.

    ü 无数据的复制。也就是说,这是一个复杂度为 O(1)的操作。通常,如果该操作改变行数或透过其他方式改变元素行索引,那么矩阵必定是连续的。参见Mat::isContinuous()。

    例如,有一存储了STL向量的三维点集,你想用3xN的矩阵来完成下面的操作:

    std::vector<Point3f> vec;

    ...

    Mat pointMat = Mat(vec). //把向量转化成Mat, 复杂度为O(1)的运算

    reshape(1). // 从Nx1的3通道矩阵得出Nx3 的单通道矩阵

    //同样是复杂度为O(1)的运算

    t(); // 最后转置Nx3 的矩阵

    //这个过程要复制所有的元素

    Mat::t

    转置矩阵。.

    C++: MatExpr Mat::t() const

    该方法通过矩阵表达式(matrix expression)实现矩阵的转置The method performs matrix transposition by means of matrix expressions. 它并未真正完成了转置但却返回一个临时的可以进一步用在更复杂的矩阵表达式中或赋给一个矩阵的转置矩阵对象:

    Mat A1 = A + Mat::eye(A.size(), A.type)*lambda;

    Mat C = A1.t()*A1; //计算(A + lambda*I)^t * (A + lamda*I).

    Mat::inv

    反转矩阵

    C++: MatExpr Mat::inv(int method=DECOMP_LU) const

    参数:

    method – 反转矩阵的方法。有以下几种可能的值:

    – DECOMP_LU是 LU 分解一定不能是单数的。

    – DECOMP_CHOLESKY 是 Cholesky LLT只适用于对称正矩阵的分解。该类型在处理大的矩阵时的速度是LU的两倍左右。

    – DECOMP_SVD是 SVD 分解。如果矩阵是单数或甚至不是2维,函数就会计算伪反转矩阵。

    该方法执行矩阵的反转矩阵表达。这意味着该方法返回一个临时矩阵反转对象并可进一步用于更复杂的矩阵表达式的中或分配给一个矩阵。

    Mat::mul

    执行两个矩阵按元素相乘或这两个矩阵的除法。

    C++: MatExpr Mat::mul(InputArray m, double scale=1) const

    参数:

    m – 与*this具有相同类型和大小的矩阵,或矩阵表达式。

    scale – 可选缩放系数。

    该方法返回一个用可选的缩放比率编码了每个元素的数组乘法的临时的对象。 注意:这不是一个对应“*”运算符的简单的矩阵乘法。.

    例::

    Mat C = A.mul(5/B); // 等价于divide(A, B, C, 5)

    Mat::cross

    计算3元素向量的一个叉乘积。

    C++: Mat Mat::cross(InputArray m) const

    参数:

    m –另一个叉乘操作对象。

    该方法计算了两个3元素向量的叉乘的积被操作向量必须是3元素浮点型的具有相同形状和尺寸的向量。结果也是一语被操作对象的具有相同形状和大小的浮点型3元素向量。

    Mat::dot

    计算两向量的点乘。

    C++: double Mat::dot(InputArray m) const

    参数:

    m –另一个点积操作对象。

    方法计算两个矩阵的点积。如果矩阵不单列或单行的向量,用顶部到底部从左到右扫描次序将它们视为 1 D向量。这些向量必须具有相同的大小和类型。如果矩阵有多个通道,从所有通道得到的点积会被加在一起。

    Mat::zeros

    返回指定的大小和类型的零数组。

    C++: static MatExpr Mat::zeros(int rows, int cols, int type)

    C++: static MatExpr Mat::zeros(Size size, int type)

    C++: static MatExpr Mat::zeros(int ndims, const int* sizes, int type)

    参数

    ndims – 数组的维数。

    rows–行数。

    cols –列数。

    size–替代矩阵大小规格Size(cols, rows)的方法。

    sizes– 指定数组的形状的整数数组。

    type– 创建的矩阵的类型。

    该方法返回一个 Matlab 式的零数组初始值设定项。它可以用于快速形成一个常数数组作为函数参数,作为矩阵的表达式或矩阵初始值设定项的一部分。

    Mat A;

    A = Mat::zeros (3,3,CV_32F);

    在上面的示例中,只要A不是 3 x 3浮点矩阵它就会被分配新的矩阵。否则为现有的

    矩阵 A填充零。

    Mat::ones

    返回一个指定的大小和类型的全为1的数组。

    C++: static MatExpr Mat::ones(int rows, int cols, int type)

    C++: static MatExpr Mat::ones(Size size, int type)

    C++: static MatExpr Mat::ones(int ndims, const int* sizes, int type)

    参数:

    ndims –数组的维数。

    rows –行数。.

    cols –列数。

    size –替代矩阵大小规格Size(cols, rows)的方法。

    sizes –指定数组的形状的整数数组。

    type –创建的矩阵的类型。

    该方法返回一个 Matlab 样式 1 的数组初始值设定项,类似Mat::zeros()。请注意,这种方法中你可以使用任意一个值和Matlab 语法初始化数组如下:

    Mat A = Mat::ones (100,100,CV_8U) * 3 ;/ / 使100 x 100 矩阵里充满 3。

    上述操作不会形成一个 100 x 100 1 的矩阵,然后乘以 3。相反,它只是记住

    缩放因子(在本例中 3)在实际调用矩阵初始值设定项时使用它。



















    展开全文
  • OpenCV—基本矩阵操作与示例

    万次阅读 多人点赞 2016-05-02 16:30:14
    图像处理中对矩阵的操作非常重要,本文总结了使用OpenCV进行矩阵的创建、初始化以及基本矩阵操作,给出了示例代码。

     

    OpenCV的基本矩阵操作与示例

     

    OpenCV中的矩阵操作非常重要,本文总结了矩阵的创建、初始化以及基本矩阵操作,给出了示例代码,主要内容包括:

     

    • 创建与初始化
    • 矩阵加减法
    • 矩阵乘法
    • 矩阵转置
    • 矩阵求逆
    • 矩阵非零元素个数
    • 矩阵均值与标准差
    • 矩阵全局极值及位置
    • 其他矩阵运算函数列表

     

     

     

    1. 创建与初始化矩阵

     

    1.1 数据类型

    建立矩阵必须要指定矩阵存储的数据类型,图像处理中常用的几种数据类型如下:

     

    CV_8UC1// 8位无符号单通道
    CV_8UC3// 8位无符号3通道
    CV_8UC4
    CV_32FC1// 32位浮点型单通道
    CV_32FC3// 32位浮点型3通道
    CV_32FC4

     

    包括数据位深度8位、32位,数据类型U:uchar、F:float型以及通道数C1:单通道、C3:三通道、C4:四通道。

     

    1.2 基本方法

    我们可以通过载入图像来创建Mat类型矩阵,当然也可以直接手动创建矩阵,基本方法是指定矩阵尺寸和数据类型:

    // 基本方法
    	cv::Mat a(cv::Size(5,5),CV_8UC1); // 单通道
    	cv::Mat b = cv::Mat(cv::Size(5,5),CV_8UC3); //3通道每个矩阵元素包含3个uchar值
    	cout<<"a  = "<<endl<<a<<endl<<endl;
    	cout<<"b  = "<<endl<<b<<endl<<endl;
    	system("pause");
    

    运行结果:

    3通道矩阵中,一个矩阵元素包含3个变量。

    1.3 初始化方法

    上述方法不初始化矩阵数据,因此将出现随机值。如果想避免这种情况,可使用Mat类的几种初始化创建矩阵的方法:

     

    // 初始化方法
    	cv::Mat mz = cv::Mat::zeros(cv::Size(5,5),CV_8UC1); // 全零矩阵
    	cv::Mat mo = cv::Mat::ones(cv::Size(5,5),CV_8UC1);  // 全1矩阵
    	cv::Mat me = cv::Mat::eye(cv::Size(5,5),CV_32FC1);  // 对角线为1的对角矩阵
    	cout<<"mz = "<<endl<<mz<<endl<<endl;
    	cout<<"mo = "<<endl<<mo<<endl<<endl;
    	cout<<"me = "<<endl<<me<<endl<<endl;
    
    运行结果:

     

     

    2. 矩阵运算

     

    2.1 基本概念

     OpenCV的Mat类允许所有的矩阵运算。

     

    2.2 矩阵加减法

    我们可以使用"+"和"-"符号进行矩阵加减运算。
    cv::Mat a= Mat::eye(Size(3,2), CV_32F);
    cv::Mat b= Mat::ones(Size(3,2), CV_32F);
    cv::Mat c= a+b;
    cv::Mat d= a-b;

     

     

     

     

    2.3 矩阵乘法

    使用"*"号计算矩阵与标量相乘,矩阵与矩阵相乘(必须满足矩阵相乘的行列数对应规则)

     

    	Mat m1= Mat::eye(2,3, CV_32F); //使用cv命名空间可省略cv::前缀,下同
    	Mat m2= Mat::ones(3,2, CV_32F);
    	cout<<"m1  = "<<endl<<m1<<endl<<endl;
    	cout<<"m2  = "<<endl<<m2<<endl<<endl;
    	// Scalar by matrix
    	cout << "\nm1.*2 = \n" << m1*2 << endl;
    	// matrix per element multiplication
    	cout << "\n(m1+2).*(m1+3) = \n" << (m1+1).mul(m1+3) << endl;
    	// Matrix multiplication
    	cout << "\nm1*m2 = \n" << m1*m2 << endl;
    

     

     

    2.4 矩阵转置

    矩阵转置是将矩阵的行与列顺序对调(第i行转变为第i列)形成一个新的矩阵。OpenCV通过Mat类的t()函数实现。
     
    // 转置
    	Mat m1= Mat::eye(2,3, CV_32F);	
    	Mat m1t = m1.t();
    	cout<<"m1  = "<<endl<<m1<<endl<<endl;
    	cout<<"m1t  = "<<endl<<m1t<<endl<<endl;
    	system("pause");
    
    运行结果:

     

     

     

    2.5 求逆矩阵

    逆矩阵在某些算法中经常出现,在OpenCV中通过Mat类的inv()方法实现
    // 求逆
    	Mat meinv = me.inv();
    	cout<<"me  = "<<endl<<me<<endl<<endl;
    	cout<<"meinv = "<<endl<<meinv<<endl<<endl;
    	system("pause");
    
    运行结果:

     

    单位矩阵的逆就是其本身。
     

     

    2.6 计算矩阵非零元素个数

    计算物体的像素或面积常需要用到计算矩阵中的非零元素个数,OpenCV中使用countNonZero()函数实现。

     

     

    // 非零元素个数
    	int nonZerosNum = countNonZero(me); // me为输入矩阵或图像
    	cout<<"me  = "<<endl<<me<<endl;
    	cout<<"me中非零元素个数 = "<<nonZerosNum<<endl<<endl;
    	system("pause");
    
    运行结果:

     

     

     

    2.7 均值和标准差

    OpenCV提供了矩阵均值和标准差计算功能,可以使用meanStdDev(src,mean,stddev)函数实现。

     

    参数

     

     

    • src – 输入矩阵或图像
    • mean – 均值,OutputArray
    • stddev – 标准差,OutputArray

     

    // 均值方差
    	Mat mean;
    	Mat stddev;
    	meanStdDev(me, mean, stddev); //me为前文定义的5×5对角阵
    	cout<<"mean = "<<mean<<endl;
    	cout<<"stddev = "<<stddev<<endl;
    	system("pause");
    

    运行结果:

    需要说明的是,如果src是多通道图像或多维矩阵,则函数分别计算不同通道的均值与标准差,因此返回值mean和stddev为对应维度的向量。

     

    	Mat mean3;
    	Mat stddev3;
    	Mat m3(cv::Size(5,5),CV_8UC3,Scalar(255,200,100));
    	cout<<"m3  = "<<endl<<m3<<endl<<endl;
    	meanStdDev(m3, mean3, stddev3);
    	cout<<"mean3 = "<<mean3<<endl;
    	cout<<"stddev3 = "<<stddev3<<endl;
    	system("pause");

    多通道矩阵运算结果:

     

     

    2.8 求最大最小值

    求输入矩阵的全局最大最小值及其位置,可使用函数:

     

     

    void minMaxLoc(InputArray src, CV_OUT double* minVal,
                               CV_OUT double* maxVal=0, CV_OUT Point* minLoc=0,
                               CV_OUT Point* maxLoc=0, InputArray mask=noArray());

     

    参数:

     

    • src – 输入单通道矩阵(图像).
    • minVal – 指向最小值的指针, 如果未指定则使用NULL
    • maxVal – 指向最大值的指针, 如果未指定则使用NULL
    • minLoc – 指向最小值位置(2维情况)的指针, 如果未指定则使用NULL
    • maxLoc – 指向最大值位置(2维情况)的指针, 如果未指定则使用NULL
    • mask – 可选的蒙版,用于选择待处理子区域

     

    // 求极值 最大、最小值及其位置
    	Mat img = imread("Lena.jpg",0);
    	imshow("original image",img);
    
    	double minVal=0,maxVal=0;
    	cv::Point minPt, maxPt;
    	minMaxLoc(img,&minVal,&maxVal,&minPt,&maxPt);
    	cout<<"min value  = "<<minVal<<endl;
    	cout<<"max value  = "<<maxVal<<endl;
    	cout<<"minPt = ("<<minPt.x<<","<<minPt.y<<")"<<endl;
    	cout<<"maxPt = ("<<maxPt.x<<","<<maxPt.y<<")"<<endl;
    	cout<<endl;
    
    	cv::Rect rectMin(minPt.x-10,minPt.y-10,20,20);
    	cv::Rect rectMax(maxPt.x-10,maxPt.y-10,20,20);
    
    	cv::rectangle(img,rectMin,cv::Scalar(200),2);
    	cv::rectangle(img,rectMax,cv::Scalar(255),2);
    
    	imshow("image with min max location",img);
    	cv::waitKey();
    
    运行结果:
     
    输入图像及其最大最小值位置

     

     

    3. 其他矩阵运算

    其他矩阵运算函数见下表:

     

     

    Function (函数名)

    Use (函数用处)

    add

    矩阵加法,A+B的更高级形式,支持mask

    scaleAdd

    矩阵加法,一个带有缩放因子dst(I) = scale * src1(I) + src2(I)

    addWeighted

    矩阵加法,两个带有缩放因子dst(I) = saturate(src1(I) * alpha + src2(I) * beta + gamma)

    subtract

    矩阵减法,A-B的更高级形式,支持mask

    multiply

    矩阵逐元素乘法,同Mat::mul()函数,与A*B区别,支持mask

    gemm

    一个广义的矩阵乘法操作

    divide

    矩阵逐元素除法,与A/B区别,支持mask

    abs

    对每个元素求绝对值

    absdiff

    两个矩阵的差的绝对值

    exp

    求每个矩阵元素 src(I) 的自然数 e 的 src(I) 次幂 dst[I] = esrc(I)

    pow

    求每个矩阵元素 src(I) 的 p 次幂 dst[I] = src(I)p

    log

    求每个矩阵元素的自然数底 dst[I] = log|src(I)| (if src != 0)

    sqrt

    求每个矩阵元素的平方根

    min, max

    求每个元素的最小值或最大值返回这个矩阵 dst(I) = min(src1(I), src2(I)), max同

    minMaxLoc

    定位矩阵中最小值、最大值的位置

    compare

    返回逐个元素比较结果的矩阵

    bitwise_and, bitwise_not, bitwise_or, bitwise_xor

    每个元素进行位运算,分别是和、非、或、异或

    cvarrToMat

    旧版数据CvMat,IplImage,CvMatND转换到新版数据Mat

    extractImageCOI

    从旧版数据中提取指定的通道矩阵给新版数据Mat

    randu

    以Uniform分布产生随机数填充矩阵,同 RNG::fill(mat, RNG::UNIFORM)

    randn

    以Normal分布产生随机数填充矩阵,同 RNG::fill(mat, RNG::NORMAL)

    randShuffle

    随机打乱一个一维向量的元素顺序

    theRNG()

    返回一个默认构造的RNG类的对象

     theRNG()::fill(...)

    reduce

    矩阵缩成向量

    repeat

    矩阵拷贝的时候指定按x/y方向重复

    split

    多通道矩阵分解成多个单通道矩阵

    merge

    多个单通道矩阵合成一个多通道矩阵

    mixChannels

    矩阵间通道拷贝,如Rgba[]到Rgb[]和Alpha[]

    sort, sortIdx

    为矩阵的每行或每列元素排序

    setIdentity

    设置单元矩阵

    completeSymm

    矩阵上下三角拷贝

    inRange

    检查元素的取值范围是否在另两个矩阵的元素取值之间,返回验证矩阵

    checkRange

    检查矩阵的每个元素的取值是否在最小值与最大值之间,返回验证结果bool

    sum

    求矩阵的元素和

    mean

    求均值

    meanStdDev

    均值和标准差

    countNonZero

    统计非零值个数

    cartToPolar, polarToCart

    笛卡尔坐标与极坐标之间的转换

    flip

    矩阵翻转

    transpose

    矩阵转置,比较 Mat::t() AT

    trace

    矩阵的迹

    determinant

    行列式 |A|, det(A)

    eigen

    矩阵的特征值和特征向量

    invert

    矩阵的逆或者伪逆,比较 Mat::inv()

    magnitude

    向量长度计算 dst(I) = sqrt(x(I)2 + y(I)2)

    Mahalanobis

    Mahalanobis距离计算

    phase

    相位计算,即两个向量之间的夹角

    norm

    求范数,1-范数、2-范数、无穷范数

    normalize

    标准化

    mulTransposed

    矩阵和它自己的转置相乘 AT * A, dst = scale(src - delta)T(src - delta)

    convertScaleAbs

    先缩放元素再取绝对值,最后转换格式为8bit型

    calcCovarMatrix

    计算协方差阵

    solve

    求解1个或多个线性系统或者求解最小平方问题(least-squares problem)

    solveCubic

    求解三次方程的根

    solvePoly

    求解多项式的实根和重根

    dct, idct

    正、逆离散余弦变换,idct同dct(src, dst, flags | DCT_INVERSE)

    dft, idft

    正、逆离散傅立叶变换, idft同dft(src, dst, flags | DTF_INVERSE)

    LUT

    查表变换

    getOptimalDFTSize

    返回一个优化过的DFT大小

    mulSpecturms

    两个傅立叶频谱间逐元素的乘法

     

    上表引自:http://blog.sina.com.cn/s/blog_7908e1290101i97z.html

     

    转载请注明出处(本文更新链接)http://blog.csdn.net/iracer/article/details/51296631


    更多内容在《机器学习原理与编程实战》中,连接原理与实战:

    https://blog.csdn.net/iracer/article/details/116051674?spm=1001.2014.3001.5501

    展开全文
  • OpenCV稀疏矩阵SparseMat

    千次阅读 2020-02-12 22:56:24
    OpenCV中一般一张图片在内存中用Mat来表述及管理,Mat内部申请一块类似与数组的内存用于存储图片中的每个像素的值即为稠密矩阵,但是有时在矩阵中其值为零的元素远远多于非为零的元素个数即稀疏矩阵,如何此时还使用...

    OpenCV中一般一张图片在内存中用Mat来表述及管理,Mat内部申请一块类似与数组的内存用于存储图片中的每个像素的值即为稠密矩阵,但是有时在矩阵中其值为零的元素远远多于非为零的元素个数即稀疏矩阵,如何此时还使用Mat进行存储 显然非常浪费空间,为了应对此中场景,OpenCV使用SparseMat类来应对稀疏矩阵场景,稀疏矩阵内部内存为一个hash表,其值为0的元素其实并没有占用内存空间,只存储其值为非零的元素,值为0的元素不占用内存空间,同时为了保证查找速度快 内部使用一个hash表进程存储。下面看来自于“Learning OpenCV3”中一段对cv::SparseMat优劣势说明

    The cv::SparseMat class is used when an array is likely to be very large compared to the number of nonzero entries.This situation often arises in linear algebra with sparse matrices, but it also comes up when one wishes to represent data, particularly histograms. in higher-dimensional arrays, since most of the space will be empty in many practical applications.A sparse representation stores only data that is actually present and so can save a great deal pf memory.In practice, many sparse objects would be too huge to represent at all in a dense format.The disadvantage of sparse representations is that computation with them is slower(on a per-element basis). This last point is important, in that computation with sparse matrices is not categorically slower. as there can be a great economy in knowing in advance that many operations need not be done at all.

    上文中指出,一个稠密性的矩阵Mat 也可以使用SparseMat来表示,只不会所使用hash 表会非常大,在相同稠密矩阵用Mat与SparseMat向比较而言,在矩阵计算中SparseMat与Mat相比会比较慢。同样一个稀疏矩阵也可以使用Mat来表示,在相同稀疏矩阵场景中,SparseMat会减少占用的内存,虽然在稠密性矩阵中SparseMat计算会比较慢,但是由于在稀疏矩阵中由于很多元素为0,其实并不会参与计算中,这部分减少的计算量在一定程度上能够抵消掉SparseMat计算性能慢的问题。

    SparseMat类支持的操作与Mat的操作在很多地方都很相似,但是又有一些不一样的操作,分块进行分析。

    SparseMat构造和拷贝函数

    SparseMat类构造函数如下:

    MethodDescription
     SparseMat()默认构造函数
    SparseMat(int dims, const int* _sizes, int _type)

    使用数组参数构造函数,与mat类似

    dims,为维度,_sizes为数组,表示每个维度大小,数组大小与维度一致。

    _type代表的是数据类型,如:CV_8U 、CV_16U 、CV_32F等

    parseMat(const SparseMat& m)拷贝函数
    explicit SparseMat(const Mat& m)参数为Mat的拷贝函数,可以利用此根据mat生成一个SparseMat

    在 SparseMat稀疏矩阵中,值为0的元素并不占用空间,所以在构造函数中初始值都为0,故实际上并未占用内存。

    构造函数和拷贝函数用例:

    const int dims = 2;
    int size[dims] = { 10, 10 };
    
    cv::SparseMat sm;
    cv::SparseMat sm1(dims, size, CV_8U);
    cv::SparseMat sm2(sm1);
    cv:Mat  m1(460, 480, CV_8UC1);
    cv::SparseMat sm3(m1);
    

    获取相关函数

     SparseMat矩阵中获取相关系列函数接口,用于获取稀疏矩阵中的参数:

    MethodDescription
    int type()

    获取矩阵元素类型,返回值为:

    #define CV_8U   0
    #define CV_8S   1
    #define CV_16U  2
    #define CV_16S  3
    #define CV_32S  4
    #define CV_32F  5
    #define CV_64F  6
    #define CV_USRTYPE1 7

    int depth()

    获取矩阵元素深度,其返回的其类型和Mat一致

    -   CV_8U = 0 - 8-bit unsigned integers ( 0..255 )
      -   CV_8S = 1 - 8-bit signed integers ( -128..127 )
        -   CV_16U = 2 - 16-bit unsigned integers ( 0..65535 )
        -   CV_16S  = 3 - 16-bit signed integers ( -32768..32767 )
        -   CV_32S  = 4 - 32-bit signed integers ( -2147483648..2147483647 )
        -   CV_32F  = 5 - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )
        -   CV_64F = 6- 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )

       -   CV_USRTYPE1=7

    int channels()返回矩阵通道数目
    const int* size()返回矩阵大小,其返回值为int *,相当于数组,数组大小与维度相同,如果矩阵还未定义大小或还未申请内存则返回NULL
    int dims()返回矩阵维度
    size_t nzcount()返回矩阵中非零元素个数,相当于hash table的节点数
    ize_t elemSize()返回每个元素占用的字节数大小(带通道),elemSize1*channels
    size_t elemSize1()返回单个元素值占用的字节数大小,其大小为elemSize()/channels()

    获取矩阵相关参数的API 大部分和Mat一样。

    用例:

    #include <stdio.h>
    #include "opencv2/opencv.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    
    using namespace cv;
    using namespace std;
    
    
    
    void main()
    {
    	const int dims = 2;
    	int size[dims] = { 10, 10 };
        
    	int number = 0;
    
    	cv::SparseMat sm;
    	cv::SparseMat sm1(dims, size, CV_8U);
    	cv::SparseMat sm2(sm1);
        cv:Mat  m1(460, 480, CV_32FC3);
    	cv::SparseMat sm3(m1);
    
    	cout << "sm type: " << sm.type() << endl;
    	cout << "sm depth: " << sm.depth() << endl;
    	cout << "sm channels: " << sm.channels() << endl;
    	const int * pSize = sm.size();
    	if (NULL == pSize)
    	{
    		cout << "sm size is empty" << endl;
    	}
    
    	cout << "sm1 type: " << sm1.type() << endl;
    	cout << "sm1 depth: " << sm1.depth() << endl;
    	cout << "sm1 channels: " << sm1.channels() << endl;
    	const int * pSize1 = sm1.size();
    	if (NULL == pSize1)
    	{
    		cout << "sm1 size is empty" << endl;
    	}
    	cout << "sm1 dims: " << sm1.dims() << endl;
    	cout << "sm1 nzcount: " << sm1.nzcount() << endl;
    
    	cout << "sm3 type: " << sm3.type() << endl;
    	cout << "sm3 depth: " << sm3.depth() << endl;
    	cout << "sm3 channels: " << sm3.channels() << endl;
    	const int * pSize3 = sm1.size();
    	if (NULL == pSize3)
    	{
    		cout << "sm3 size is empty" << endl;
    	}
    
    	cout << "sm3 dims: " << sm3.dims() << endl;
    	cout << "sm3 nzcount: " << sm3.nzcount() << endl;
    	cout << "sm3 elemSize: " << sm3.elemSize() << endl;
    	cout << "sm3 elemSize1: " << sm3.elemSize1() << endl;
    }

    运行结果:

    SparseMat访问矩阵数据

    由于SparseMat类种的矩阵数据存储方式是采用的hash表方式,与Mat矩阵采用数组方式进行存储不同,所以两者在矩阵的读写方式上也有很大不同,SparseMat类有自己特有的读取矩阵方式,分别为ptr、ref, find, value。

    ptr读写数据方式

    SparseMat支持指针访问方式,其输入参数分别为相应维度下面的index,直接返回元素的地址,这点与Mat ptr类似,但是返回的指针为单个元素的地址,而不能采用Mat中的ptr方式根据ptr指针可以获取后面所有的元素,仅仅返回的是相应元素的地址,不可以通过指针偏移的方式访问到下一个元素地址,这是与Mat中的ptr最大的不同。因为SparseMat矩阵中存储的并不是连续的,而是一张hash表。

    SparseMat相关ptr的API,分别根据维度提供不同的API:

    MethodDescription
     uchar* ptr(int i0, bool createMissing, size_t* hashval=0)

    一维矩阵

    i0:一维矩阵元素index

    createMissing: 如果id0元素没有申请内存,是否申请内存。1:申请内存,0:不申请内存

    返回值为元素地址,如果不申请内存,且元素为0,则返回值为NULL

    hashval: hash 表key值。如果hashval为0,则在查找的过程中首先要根据i0,计算hash key值,再查找hash表。

                 因为hash表查找过程,时间复杂度为O(1),性能都浪费在计算hash key值,如果想提高效率,可以提前计算获取到                hash值,能够提高算法效率

    uchar* ptr(int i0, int i1, bool createMissing, size_t* hashval=0)

    二维矩阵

    i0和i1 两个维度下的index

    uchar* ptr(int i0, int i1, int i2, bool createMissing, size_t* hashval=0)

    三维矩阵

    i0,i1和i2为各自维度下的index

    uchar* ptr(const int* idx, bool createMissing, size_t* hashval=0)idx:将各维度采用数组方式,其数组大小必须与 矩阵维度一致。

    针对返回值,OpenCV源代码中给出了详细解释:

    specialized variants for 1D, 2D, 3D cases and the generic_type one for n-D case.
         return pointer to the matrix element.
          - if the element is there (it's non-zero), the pointer to it is returned
          - if it's not there and createMissing=false, NULL pointer is returned
          - if it's not there and createMissing=true, then the new element
            is created and initialized with 0. Pointer to it is returned
          - if the optional hashval pointer is not NULL, the element hash value is
            not computed, but *hashval is taken instead.

    •  当元素值不为0,且在hash表中存在,则指针直接返回改元素的地址
    • 当createMissing 设为false,且该元素不存在hash表中,则直接返回空指针NULL
    • 当createMissing 设为true,且该元素不存在hash表中,则将会为该元素申请一个新的空间,并初始化为0,并将该元素的地址返回
    • 如果hashval 参数不为空,则不用计算该元素的hash值,直接使用hashval 作为hash key,直接查表

     ptr相关用例

    用例1:创建一个二维数组 10*10,数据类型为CV_8U, 使用ptr指针进行遍历,prt中参数createMissing=false 查看是否全部为空

    #include "opencv2/imgproc/imgproc.hpp"
    
    using namespace cv;
    using namespace std;
    
    void main()
    {
    	const int dims = 2;
    	int size[dims] = { 10, 10 };
        
    	int number = 0;
    
    	cv::SparseMat sm;
    	cv::SparseMat sm1(dims, size, CV_8U);
    
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{		
    			if (NULL == sm1.ptr(i, j, 0))
    			{
    				printf("sm1 is ptr\n");
    			}			
    	}
    }

    结果:

    用例2:创建一个二维数组 10*10,数据类型为CV_8U, 使用ptr指针进行遍历,prt中参数createMissing=true ,并对每个元素赋值:

    #include <stdio.h>
    #include "opencv2/opencv.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    
    using namespace cv;
    using namespace std;
    
    
    
    void main()
    {
        const int dims = 2;
        int size[dims] = { 10, 10 };
        
        int number = 0;
    
        cv::SparseMat sm;
        cv::SparseMat sm1(dims, size, CV_8U);
    
        for (int i = 0; i < 10; i++)
        {
            for (int j = 0; j < 10; j++)
    	{
    	    *sm1.ptr(i, j, 1) = number;
    	    number++;
    	}
        }
    
        for (int i = 0; i < 10; i++)
        {
    	for (int j = 0; j < 10; j++)
    	{
    	    printf("id0: %d, id1:%d, value:%d\n",i,j, *sm1.ptr(i, j, 1));
    	}
        }
    
        cout << "sm1 element number:" << sm1.nzcount() << endl;
    }

    运行结果:

     

     用例3:创建一个二维数组 10*10,数据类型为CV_32F, 使用ptr指针进行遍历,prt中参数createMissing=true ,并对每个元素赋值:

    #include <stdio.h>
    #include "opencv2/opencv.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    
    using namespace cv;
    using namespace std;
    
    void main()
    {
    	const int dims = 2;
    	int size[dims] = { 10, 10 };
        
    	int number = 0;
    
    	cv::SparseMat sm;
    	cv::SparseMat sm1(dims, size, CV_32F);
    
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			*(float *)sm1.ptr(i, j, 1) = number*3.14;
    			number++;
    		}
    	}
    
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			printf("id0: %d, id1:%d, value:%f\n",i,j, *(float *)sm1.ptr(i, j, 0));
    		}
    	}
    
    	cout << "sm1 element number:" << sm1.nzcount() << endl;

    运行结果:

      用例4:创建一个二维数组 10*10,数据类型为CV_32F, 使用ptr指针进行遍历,prt中参数createMissing=true ,并对每个元素赋值,使用hash值对每个元素进行遍历:

    #include <stdio.h>
    #include "opencv2/opencv.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    
    using namespace cv;
    using namespace std;
    
    
    
    void main()
    {
    	const int dims = 2;
    	int size[dims] = { 10, 10 };
        
    	int number = 0;
    
    	cv::SparseMat sm;
    	cv::SparseMat sm1(dims, size, CV_32F);
    
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			*(float *)sm1.ptr(i, j, 1) = number*3.14;
    			number++;
    		}
    	}
    
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			size_t hash = sm1.hash(i, j);
    			printf("id0: %d, id1:%d, value:%f\n",i,j, *(float *)sm1.ptr(i, j, false, &hash));
    		}
    	}
    
    	cout << "sm1 element number:" << sm1.nzcount() << endl;
    }

    运行结果:

     ref读写引用方式

    ref为SparseMat矩阵中的元素另外一种访问方式,相当于*(_Tp*)ptr(i0,...,true[,hashval]) 方式,如果矩阵中的元素在hash表中不存在,则新申请一个并返回0,其API如下:

    MethodDescription
    template<typename _Tp> _Tp& ref(int i0, size_t* hashval=0)一维数组访问
    template<typename _Tp> _Tp& ref(int i0, int i1, size_t* hashval=0)二维数组访问
    template<typename _Tp> _Tp& ref(int i0, int i1, int i2, size_t* hashval=0)三维数组访问
    template<typename _Tp> _Tp& ref(const int* idx, size_t* hashval=0)数组参数形式,其数组大小必须和dim一致

    各个入参的意思和ptr参数一致,与ptr相比ref相当于一个模板能够适应各种数据类型,而ptr中需要根据相应的数据类型进行强转很容易出现错误,而ref可以避免这种错误。

    ref用例

    ref的大部分用法和ptr类似,列举一个10*10的矩阵,并对其赋值以及读取:

    #include <stdio.h>
    #include "opencv2/opencv.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    
    using namespace cv;
    using namespace std;
    
    
    
    void main()
    {
    	const int dims = 2;
    	int size[dims] = { 10, 10 };
        
    	int number = 0;
    
    	cv::SparseMat sm;
    	cv::SparseMat sm1(dims, size, CV_32F);
    
    	size_t* hashval = NULL;
    
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			sm1.ref<float>(i,j, hashval) = number*3.14;
    			number++;
    		}
    	}
    
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			printf("id0: %d, id1:%d, value:%f\n",i,j, sm1.ref<float>(i, j, hashval));
    		}
    	}
    
    	cout << "sm1 element number:" << sm1.nzcount() << endl;

    结果ptr中的用例一致。

    find读取元素方式

    find方式为只读取元素的方式,不可以对其元素赋值,相当于(_const Tp*)ptr(i0,...false[,hashval]), 返回的指针指向为一个常量,所以只可读不可写,相关API如下:

    MethodDescription
     template<typename _Tp> const _Tp* find(int i0, size_t* hashval=0) const一维矩阵
    template<typename _Tp> const _Tp* find(int i0, int i1, size_t* hashval=0) const二维矩阵
    template<typename _Tp> const _Tp* find(int i0, int i1, int i2, size_t* hashval=0) const三维矩阵
    template<typename _Tp> const _Tp* find(const int* idx, size_t* hashval=0) const参数为数组的读取函数,数组大小必须与dim一致

    find方式只可读,不可写,返回为一个指针常量,find也是一个模板函数,支持常用的数据类型,如果该元素不存在,返回NULL.

    find用例

    列举一个10*10的矩阵,使用ref对矩阵进行赋值,用find读取元素。

    #include <stdio.h>
    #include "opencv2/opencv.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    
    using namespace cv;
    using namespace std;
    
    
    
    void main()
    {
    	const int dims = 2;
    	int size[dims] = { 10, 10 };
        
    	int number = 0;
    
    	cv::SparseMat sm;
    	cv::SparseMat sm1(dims, size, CV_32F);
    
    	size_t* hashval = NULL;
    
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			sm1.ref<float>(i,j, hashval) = number*3.14;
    			number++;
    		}
    	}
    
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			printf("id0: %d, id1:%d, value:%f\n",i,j, *sm1.find<float>(i, j, hashval));
    		}
    	}
    
    	cout << "sm1 element number:" << sm1.nzcount() << endl;
    }

    运行结果和ptr用例一致。

    value读取元素方式

    value方式为另外一种读取矩阵元素的方式,也是只可读不可写,相当于

     const _Tp* p = find<_Tp>(i0,...[,hashval]); return p ? *p : _Tp();

    与find不同的是,函数直接返回的是元素的值而不是指针, API如下:

    MethodDescription
    template<typename _Tp> _Tp value(int i0, size_t* hashval=0) const一维元素读取
    template<typename _Tp> _Tp value(int i0, int i1, size_t* hashval=0) const二维元素读取
    template<typename _Tp> _Tp value(int i0, int i1, int i2, size_t* hashval=0) const三维元素读取
    template<typename _Tp> _Tp value(const int* idx, size_t* hashval=0) const入参为数组的元素读取,数组大小必须与dim一直

     value函数用法与find一样

    value用例

    列举一个10*10的矩阵,使用ref对矩阵进行赋值,用value读取元素。

    #include <stdio.h>
    #include "opencv2/opencv.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    
    using namespace cv;
    using namespace std;
    
    
    
    void main()
    {
    	const int dims = 2;
    	int size[dims] = { 10, 10 };
        
    	int number = 0;
    
    	cv::SparseMat sm;
    	cv::SparseMat sm1(dims, size, CV_32F);
    
    	size_t* hashval = NULL;
    
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			sm1.ref<float>(i,j, hashval) = number*3.14;
    			number++;
    		}
    	}
    
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			printf("id0: %d, id1:%d, value:%f\n",i,j, sm1.value<float>(i, j, hashval));
    		}
    	}
    
    	cout << "sm1 element number:" << sm1.nzcount() << endl;
    }

    value函数用法与find一样

    迭代访问

    SparseMat还支持利用迭代器对数组进行迭代访问,SparseMat中的迭代只能变量访问hash表,并不能创建新的hash表。

    共有四个迭代器SparseMatIterator、SparseMatIterator_、SparseMatConstIterator、SparseMatConstIterator_

    MethodDescription
    SparseMatIterator begin()SparseMatIterator迭代器开始
    template<typename _Tp> SparseMatIterator_<_Tp> begin()SparseMatIterator_迭代器模板开始
    SparseMatConstIterator begin() constSparseMatConstIterator迭代器开始
    template<typename _Tp> SparseMatConstIterator_<_Tp> begin() constSparseMatConstIterator_迭代器开始
     SparseMatIterator end()SparseMatIterator 迭代器结束位置
    SparseMatConstIterator end()SparseMatConstIterator 迭代器结束位置
    template<typename _Tp> SparseMatIterator_<_Tp> end()SparseMatIterator_迭代器结束位置
    template<typename _Tp> SparseMatConstIterator_<_Tp> end()SparseMatConstIterator_迭代器结束
     template<typename _Tp> _Tp& value(Node* n)根据迭代节点获取相应元素值 模板,可读可改
    template<typename _Tp> const _Tp& value(const Node* n)根据迭代器节点获取相应元素值,只可读不可改

    SparseMat中每个迭代器都有一个迭代节点,每个节点Node记录相对应元素的hashval值,以及对应的元素在矩阵中的位置,Node数据结构如下:

        struct CV_EXPORTS Node
        {
            //! hash value
            size_t hashval;
            //! index of the next node in the same hash table entry
            size_t next;
            //! index of the matrix element
            int idx[MAX_DIM];
        };

    可以根据Node中的hashval值直接查找到对应元素的值,或者根据封装好的value(Node* n)节点获取到相对应的值。

    迭代用例

    迭代用例如下:

    #include <stdio.h>
    #include "opencv2/opencv.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    
    using namespace cv;
    using namespace std;
    
    
    
    void main()
    {
    	const int dims = 2;
    	int size[dims] = { 10, 10 };
        
    	int number = 0;
    
    	cv::SparseMat sm;
    	cv::SparseMat sm1(dims, size, CV_32F);
    
    	size_t* hashval = NULL;
    	
    	
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			sm1.ref<float>(i, j, hashval) = number*3.14;
    			number++;
    		}
    	}
    
    	cv::SparseMatIterator_<float> it = sm1.begin<float>();
    	cv::SparseMatIterator_<float> it_end = sm1.end<float>();
    	for (; it != it_end; ++it)
    	{	
    		const cv::SparseMat::Node * node = it.node();
    		printf("id0: %d, id1:%d, value:%f\n", node->idx[0], node->idx[1], sm1.value<float>(it.node()));
    		++number;
    	}
    	
    
    	cout << "sm1 element number:" << sm1.nzcount() << endl;
    }

    运行结果:

    可以看到节点的存储并不是按照矩阵的索引进行排序。 

    hash获取hash值

    SparseMat提供接口根据索引index获取到hash表中对应的hash key值,API如下:

    MethodDescription
     size_t hash(int i0)

    一维矩阵获取hash key

    i0:矩阵index

    size_t hash(int i0, int i1)

    二维矩阵获取hash key

    i0和i1为二维矩阵的各维度的索引

     size_t hash(int i0, int i1, int i2)

    三维矩阵获取hash key

    i0、i1和i2为三维矩阵各维度的索引

    size_t hash(const int* idx)

    参数为数组形式的获取hash key,

    其中数组大小必须与维度一致。

    hash()函数用法相对比较简单,提前获取到hash key值,在获取或设置hash表中的值时,配合ptr, ref等函数时可以节约根据索引计算hash值得时间,从而提高效率。

    erase删除hash表某个元素

    SparseMat与Mat中还有一个最大的不同,就是可以根据需要删除hash表中的某个元素,API:

    MethodDescription
    void erase(int i0, int i1, size_t* hashval=0)二维数组删除hash表中的某个元素
    void erase(int i0, int i1, int i2, size_t* hashval=0)三维数组删除hash表中的元素
    void erase(const int* idx, size_t* hashval=0)数组形式删除hash表中的元素

    erase用例

    #include <stdio.h>
    #include "opencv2/opencv.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    
    using namespace cv;
    using namespace std;
    
    
    
    void main()
    {
    	const int dims = 2;
    	int size[dims] = { 10, 10 };
        
    	int number = 0;
    
    	cv::SparseMat sm;
    	cv::SparseMat sm1(dims, size, CV_32F);
    
    	size_t* hashval = NULL;
    	
    	
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			sm1.ref<float>(i, j, hashval) = number*3.14;
    			number++;
    		}
    	}
    
    	cout << "sm1 element number:" << sm1.nzcount() << endl;
    	cout << "Delete all element..." << endl;
    
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			sm1.erase(i, j);
    			number++;
    		}
    	}
    	
    
    	cout << "sm1 element number:" << sm1.nzcount() << endl;
    }

     运行结果:

     clear清除hash表

    SparseMat矩阵支持clear一次性清除所有hash表,将所有元素值清为0,并释放所有hash表内存

    void clear();

    SparseMat与Mat相互转换

    SparseMat支持与稠密矩阵Mat转换,API如下:

    MethodDescription
    void copyTo( Mat& m )将SparseMa矩阵转成Mat矩阵
    void convertTo( Mat& m, int rtype, double alpha=1, double beta=0 )

    将SparseMat 矩阵转成Mat矩阵m, rtype为转换的矩阵type,

    alpha和beta是否需要将值进行转换计算,计算公式如下:

    alpha* element + beta

    alpha默认为1, beta默认为0,相当于默认情况下时等值转换,此时相当于copyTo

     SparseMat(const Mat& m)根据Mat 创建 SparseMat矩阵

    Mat矩阵转成SparseMat用例 

    创建一个640*480,通道为1的uchar的Mat,其值为0,将其转换到SparseMat

    #include <stdio.h>
    #include "opencv2/opencv.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    
    using namespace cv;
    using namespace std;
    
    
    
    void main()
    {
        cv:Mat  m1(460, 480, CV_8UC1);
    	cv::SparseMat sm1(m1);
    	
    	cout << "sm1 element number:" << sm1.nzcount() << endl;
    }

     根据Mat创建SparseMat矩阵时,即使Mat的数据全为0,SparseMat矩阵也会创建内存空间,运行结果:

    sm1 element number:220800

    SparseMat矩阵转换成Mat 用例:

    copyTo和convertTo都支持转换成Mat,copyTo至少相当于copy,而convertTo支持修改输出的Mat类型及值,如果Mat没有内存空间,则首先申请内存空间,然后再进行转换

    #include <stdio.h>
    #include "opencv2/opencv.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    
    using namespace cv;
    using namespace std;
    
    
    
    void main()
    {
    	const int dims = 2;
    	int size[dims] = { 10, 10 };
        
    	int number = 0;
    
    	cv::SparseMat sm1(dims, size, CV_32F);
    
    	size_t* hashval = NULL;
    	
    	
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			sm1.ref<float>(i, j, hashval) = number*3.14;
    			number++;
    		}
    	}
    
    	Mat m1;
    	Mat m2;
    	sm1.copyTo(m1);
    	sm1.convertTo(m2, CV_32F);
    
    	cout << "m1 element number:" << m1.total() << endl;
    	cout << "m2 element number:" << m2.total() << endl;
    }

    运行结果:

     m1 element number:100
             m2 element number:100

    copyTo和convertTo

    SparseMat还支持SparseMat函数copy,以及根据源SparseMat 生成目的SparseMat,且修改目的SparseMat的类型

    MethodDescription
    copyTo( SparseMat& m )copy到矩阵m,如果m矩阵中之前存在内容,则将旧的全部清空
     void convertTo( SparseMat& m, int rtype, double alpha=1 ) 

    将所有的矩阵转成成按照需要的rtype,且所有矩阵的值乘以一个系数alpha,可以用来在转换过程中数值越界

    alpha默认为1

    用例

    上述两个函数相关用例:

    #include <stdio.h>
    #include "opencv2/opencv.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    
    using namespace cv;
    using namespace std;
    
    
    
    void main()
    {
    	const int dims = 2;
    	int size[dims] = { 10, 10 };
        
    	int number = 0;
    
    	cv::SparseMat sm1(dims, size, CV_32F);
    
    	size_t* hashval = NULL;
    	
    	
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			sm1.ref<float>(i, j, hashval) = number*3.14;
    			number++;
    		}
    	}
    
    	cv::SparseMat sm2;
    	cv::SparseMat sm3;
    	sm1.copyTo(sm2);
    	sm1.convertTo(sm3, CV_32F);
    	cout << "sm1 element number:" << sm1.nzcount() << endl;
    	cout << "sm2 element number:" << sm2.nzcount() << endl;
    	cout << "sm3 element number:" << sm3.nzcount() << endl;
    }

    运行结果:

    sm1 element number:100
           sm2 element number:100
           sm3 element number:100

     SparseMat矩阵头

    SparseMat内部使用hdr用于存储矩阵头相关信息,矩阵头主要内容如下:

       struct CV_EXPORTS Hdr
        {
            Hdr(int _dims, const int* _sizes, int _type); //Hdr构造函数
            void clear(); //一次性清除所有hash
            int refcount;//hashtabe内存引用计数,当为0使,直接是否内存
            int dims; //矩阵维度
            int valueOffset;// node不包含hash 值大小
            size_t nodeSize; //node 占用空间大小,包含hash 值
            size_t nodeCount;//node节点数目
            size_t freeList;
            std::vector<uchar> pool;//node 节点地址
            std::vector<size_t> hashtab; //hashtabe
            int size[MAX_DIM];
        };

      operator = 重构

    SparseMat矩阵支持=等号重构,如下:

        //! assignment operator. This is O(1) operation, i.e. no data is copied
        SparseMat& operator = (const SparseMat& m);
        //! equivalent to the corresponding constructor
        SparseMat& operator = (const Mat& m);

    支持 SparseMat两个矩阵直接赋值,也支持等号右值为Mat矩阵

     

    展开全文
  • OpenCV的基本矩阵操作与示例 OpenCV中的矩阵操作非常重要,本文总结了矩阵的创建、初始化以及基本矩阵操作,给出了示例代码,...建立矩阵必须要指定矩阵存储的数据类型,图像处理中常用的几种数据类型如下: CV_8...
  • 6、OpenCV掩码矩阵运算Mask

    千次阅读 2021-01-08 13:56:12
    OpenCV掩码矩阵运算Mask一、学习目标二、掩码矩阵运算三、两种解决方案四、完整代码示例五、致谢 一、学习目标 了解什么是掩码矩阵运算 学会2种方法实现掩码矩阵运算 使用锐化图像的实例 二、掩码矩阵运算 矩阵的...
  • Opencv Mat 矩阵的运算

    千次阅读 2017-08-07 17:34:29
    OpenCV的基本矩阵操作与示例 ...OpenCV中的矩阵操作非常重要,本文总结了矩阵的创建、初始化以及基本矩阵操作,给出了示例代码,主要内容包括: ...创建与初始化矩阵加减法矩阵...建立矩阵必须要指定矩阵存储的数据
  • opencv矩阵计算的一些函数

    千次阅读 2017-05-17 09:42:43
    综述:OpenCV有针对矩阵操作的C语言函数. 许多其他方法提供了更加方便的C++接口,其效率...矩阵按行存储,每行有4字节的校整. //由于opencv矩阵式一位数组或者一位指针,所以我们只能利用opencv的函数对矩阵元素进行操
  • openCV矩阵1

    2012-02-16 22:53:53
    CvMat操作 分配释放矩阵空间 ...OpenCV有针对矩阵操作的C语言函数....矩阵按行存储,每行有4字节的校整. 分配矩阵空间: CvMat* cvCreateMat(int rows, int cols, int type); type: 矩阵元素类型.
  • OpenCV矩阵上的卷积

    千次阅读 2014-08-09 01:59:21
     在openCV官网上说是戴面具,其实就是重新计算一下矩阵中的每一个value,那么怎么计算呢,根据该像素点的周围信息,用一个加权的公式来进行计算。那么现在就要看,周围的信息是如何被加权的。让我们想一下这样的...
  • OpenCV Mat矩阵(图像Mat)初始化及访问方法

    万次阅读 多人点赞 2019-06-20 17:39:50
    一、Mat初始化 1.使用Mat构造函数 ...//其实是2*6的矩阵,因为每个元素有3个通道。 Mat M1( 2, 2, CV_8UC1,Scalar(0) );//单通道 //方法二: int sz[3] = {2, 2, 2}; Mat L( 3, sz, CV_8UC(1), Scalar::all(...
  • OpenCV矩阵类详解:Mat

    千次阅读 2017-11-30 10:05:35
    转载自:http://www.xuebuyuan.com/2148247.htmlOpenCV中矩阵类详解之一:Mat综述Mat类可以被看做是opencv中C++版本的矩阵类,替代原来C版本的矩阵结构体CvMat和图像结构体IplImage;Mat最大的优势跟STL的兼容性很好...
  • opencv矩阵的用法

    2010-11-02 19:27:00
    转自:http://hi.baidu.com/xiaoduo170/blog/item/10fe5e3f0fd252e455e72380.html每回用矩阵都要查,这回查到一个比较... 矩阵按行存储,每行有4字节的校整. 分配矩阵空间: CvMat* cvCreateMat(int rows, int cols, in
  • OpenCV 旋转矩阵RotatedRect介绍

    千次阅读 2019-07-29 11:53:00
    旋转矩阵由一个中心点(或者质量重心),每条边的长度和一个旋转角构成。 下面给出例子 // 图像,八位三通道,黑色 Mat image(200, 200, CV_8UC3, Scalar(0)); // 构建旋转矩阵,位置,大小,旋转角度 ...
  • opencv里有多种矩阵的创建方法。最常见的方法是cvCreateMat(),由多个原函数组成,如cvCreateMatHeader()和cvCreateData()。前者负责创建cvMat结构,不为数据分配内存,而后者只负责数据的内存分配。有时,只需要...
  • OpenCV矩阵类详解之二:CvMat

    千次阅读 2014-01-02 11:26:59
    OpenCV将向量作为1维矩阵处理,矩阵按行存储,每行有若干字节的校整;在较早版本里面,CvMat使用的是C语言实现的struct,较新的版本里面有C++实现的class(Mat类)。 CvMat的定义 typedef struct CvMat { int type...
  • openCV矩阵的读取

    千次阅读 2012-02-19 16:28:06
    OpenCV中有三种方式访问矩阵中的数据元素:容易的方式,困难的方式,以及正确的方式。以下先讲容易的方式和困难的方式。 容易的方式 最容易的方式是使用宏CV_MAT_ELEM( matrix, elemtype, row, col ),输入参数是...
  • OpenCV: 矩阵等对象的文件存取方式

    千次阅读 2016-11-02 16:18:30
    OpenCV中的文件存取问题描述在做图像处理等工作时,经常需要对矩阵Mat等各类对象进行存取,那么在OpenCV中有没有合适的方法呢?解决方案OpenCV中保存图片的最常用方式是imwrite(),但是只能将矩阵按8位/24位图片格式...
  • opencv矩阵操作

    2017-03-15 11:09:09
    opencv基本矩阵操作
  • 一、最简单的方法——利用宏 两种宏: (1)CV_MAT_ELEM() CvMat* mat = cvCreateMat(5,5,CV_32FC1);...一共三个参数,第一个参数是传入的矩阵,第二个参数是待返回元素的行,最后一个是列。 (2)CV_MAT_ELEM_P
  • OpenCV矩阵Mat的小分析

    千次阅读 2014-11-20 10:02:42
    OpenCV中经常要进行对矩阵的操作,
  •  因为要对一个矩阵求特征向量矩阵和特征值,本想使用MTL库,因为MTL从成熟程度和运算效率都有保证,没想到使用MTL库求特征向量矩阵和特征值还要依赖其它库,只好另辟蹊径了。幸好我找到了OpenCV,一个由intel资助的...
  • opencv矩阵的操作

    千次阅读 2009-10-30 10:13:00
    http://www.opencv.org.cn/index.php/Cxcore%E6%95%B0%E7%BB%84%E6%93%8D%E4%BD%9C#SolveGEMM通用矩阵乘法void cvGEMM( const CvArr* src1, const CvArr* src2, double alpha, const CvArr* src3
  • openCV多维矩阵的内存分布

    千次阅读 2014-04-26 10:28:07
    为了高效的获取矩阵形式存在的多维... 例如,使用矩阵存储n个三维点,有如下四种可能的方式:  1、n 行 1 列,3 通道;  2、1 行 n 列,3 通道;  3、n 行 3 列,1 通道;  4、3 行 n 列,1 通道;
  • OpenCv 关于矩阵的相关计算函数

    千次阅读 2018-03-31 14:32:44
    GEMM通用矩阵乘法void cvGEMM( const CvArr* src1, const CvArr* src2, double alpha, const CvArr* src3, double beta, CvArr* dst, int tABC=0 ); #define cvMatMulAdd( src1, src2, src3, dst ) cvGEMM( src1,...
  • Opencv矩阵的掩码操作

    千次阅读 2014-04-22 22:05:59
    矩阵的掩码操作很简单。其思想是:根据掩码矩阵(也称作核)重新计算图像中每个像素的值。掩码矩阵中的值表示近邻像素值(包括该像素自身的值)对新像素值有多大影响。从数学观点看,我们用自己设置的权值,对像素...
  • Opencv 矩阵基础

    2016-08-24 16:25:30
    Opencv 矩阵基础简介

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,017
精华内容 6,006
关键字:

opencv存储矩阵