您的位置:首页 > 编程语言 > Java开发

opencv(Java)的矩阵创建与基础操作

2015-03-20 23:11 316 查看
opencv(Java)的矩阵创建与操作

转自: /article/3456419.html

(1)创建矩阵

OpenCV Java中矩阵就是一个类 Mat,和它扩展出来的MatOfDouble之类的类。通常创建一个矩阵对象的方法是:

Mat A = new Mat(3, 4, CvType.CV_64FC1);

或者分两步:

Mat A = new Mat();

A.create(3,4,CvType.CV_64FC1);

矩阵使用完毕不用了,要记得销毁:

A.release();

另外还有些特殊矩阵的创建方法,这个文档里倒是有,大家可以看看。这些方法一般是静态方法,可以通过类来调用。例如:

A = Mat.eye(3,3, CvType.CV_64FC1);

A = Mat.zero(3,3,CvType.CV_64FC1);

诸如MatOfDouble之类的矩阵,还支持将矩阵转换为List或者从List(array)转换成矩阵。所以可以直接这样创建矩阵:

MatOfDouble A = new MatOfDouble(1,2,3,4,5,6,7,8,9);

或者

MatOfDouble A = new MatOfDouble();

A.fromArray(1,2,3,4,5,6,7,8,9);

注意,这样得到的矩阵实际上是一个列向量,也就是9*1的矩阵。如果想得到一个3*3的矩阵,可以用reshape,

A.reshape(1,3);

reshape这个方法的参数是很坑爹的,我好长时间都以为参数是行和列,后来翻文档才发现,第一个参数是通道数,第二参数是行数。不怪别人,是我自己想当然了。

另外,Mat还有一个方法是获得矩阵尺寸的,size()。该方法也十分坑爹,返回值居然是列*行。比方说上面那个矩阵A,

System.out.println(A.size().toString());

结果是1x9。

(2) 对矩阵进行操作是很简单的,因为OpenCV有专门的方法,其他的不说了,有一个特别要命,主要是Java开发人员特别要小心的,就是矩阵的乘法。对于C++开发者而言,矩阵乘法可以直接用*搞定,例如A=B*C。Java语言是不支持运算符重载的,因此也就不可能用这么简单的形式实现,只能用方法实现。

不过你找javadoc的时候会发现,Mat类里也提供了multi的方法,哈哈……且慢欢喜,这个不是我们说的矩阵乘法,而是矩阵对应元素的乘法,真正的矩阵乘法在Core类里,需要用Core.gemm()的形式调用。我写了一个简单那的函数实现了简单的矩阵乘法:

/**

* 计算矩阵A和B的乘,得到新的矩阵C。即 C=A*B;

* 注意:A,B,C调用前必需要初始化完毕。

* @param A 被乘矩阵

* @param B 乘矩阵

* @param C 结果矩阵

* @return 矩阵C

*/

public Mat matMul(Mat A, Mat B, Mat C)

{

Core.gemm(A, B, 1.0, Mat.zeros(A.size(), A.type()), 0.0, C);

return C;

}

这个方法是有文档的,不说啥了,目前还没有找到其他更好的方法。注意Mat类里还有dot()方法和cross()方法,都是给向量准备的,一个点乘,一个叉乘,其中叉乘还只能是三元素的向量。

(3) Java矩阵类的粗浅解析

我的理解Java矩阵实际上是两个部分。一个部分是Java的矩阵说明,包括尺寸,通道数,数据类型等;另一部分是实际存储数据的区域,在Java中这部分应该是用JNI调用其C++版本里的功能实现的,所以需要create和release。因此在做矩阵复制,赋值,转换等的各种操作的时候,有的时候是完整的创建了一个新的对象,有的时候只是创建了一个Java头,数据仍然存储在原来的地方没动,所以就有可能我们创建了好多个Mat对象,而实际存储的区域可能没那么多,会有多个Mat对象引用同一块数据存储区域。release方法实际上内部也有一个类似计数的变量,每次调用release的时候就会将这个变量减一,用这种方法来保证在合适的时候释放内存。所以这么说适时的调用release还是很重要的。但实际上什么时候应该release确实是个难题。我感觉一个标准是,谁new的对象,谁就负责release。在函数内部创建的矩阵对象,当做返回值的,必须要告诉调用者去release。

(4)矩阵元素的读取和存储

Java里读取矩阵的元素和改变某位置元素的值非常麻烦。对于C++而言,A(0,0)就可以直接取出(0,0)的值,同时也可以直接赋值。Java专门有这么两个方法,就是put()和get()。例如:

double[] x = A.get(0, 0)

这个操作可以获得(0,0)位置上的元素的值。为什么是返回的数组呢?因为OpenCV的矩阵可以是多维的,也就是在(0,0)位置上的值可以有多个通道(典型的例子是图像,其元素可能是3个值的数组(R,G,B)或者4个值的数组),所以返回值是数组。对于单通道的数据类型的矩阵而言,x[0]就是它的值。

A.put(0, 0, 1.0)

这个操作将(0,0)的值设置为1.0。当然本之上也应该是一个数组的,因为这里Java定义为可变数量参数,所以也可以只输入一个1.0。

但是这样的操作太麻烦了,大家可以想象一下最简单的常用的4*3矩阵如果这么赋起值来,那出错的概率是杠杠的。

还有一个办法就是把矩阵先转换为数组,然后进行操作,操作完毕后再转换会矩阵。也就是用数组作为中转,因为Java中操作数组还是很容易的。

Mat没有提供一个专为数组的方法,只能自己写一个了。其实也很简单,还是用的上面的两个方法。

double[] value = new double[9];

A.get(0,0, value); //从(0,0)位置开始读取数据来填充数组,一直到数组满或者矩阵结尾

或者

double[] value = new double[]{1,2,3,4,5,6,7,8,9};

A.put(0,0,value); //从(0,0)位置开始填充数组里的数据,一直到数组结束

很方便吧。注意计算好数组的大小。用这种方法也可以修改矩阵的部分数据。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: