OpenCV组件结构
关于OpenCV的组件结构“浅墨”大神给出了详细的解释,详细的参照:一览众山小:OpenCV 2.4.8 or OpenCV 2.4.9组件结构全解析 但在OpenCV3.1中还是有些变化的,在opencv/build/include/opencv2目录下有OpenCV的所有模块,
一共有这么多模块,在浅墨的文章中也做了详细的介绍,有些模块已经没有了。然后再看看java中所包含的模块,可以从opencv.jar中看到所实现的模块:
明显看到跟C++版本的少了很多模块,但是基本的功能模块都已经有了,官方应该也在不断晚上java版本的接口,不久的将来也应该会拥有跟C++版本一模一样的功能吧。可以到各个模块中查看所实现的方法,一般的方法中都有注释,而且方法的名字都是跟C++中是一样的。
我们知道一幅图片都是有不同的像素点组成的,每个像素点的值代表了一个颜色,比如一副灰度图像,从0(黑色)到255(白色)来代表颜色的深浅。而通常一幅彩色图像又是由红绿蓝三种颜色组成,俗称RGB图像。更多关于图像与颜色可以参考数字图像处理方面的书籍,这是介绍OpenCV是如何来表示不同的图像的。在core->CvType中定义了很多关于图像类型的常量,基本结构是:
CV_<位数>{U|S|F}C(<通道数>) 位数=8|16|32|64 通道数=1|2|3|4 最多只有四通道。 下面以一通道的来做详细的说明。
- CV_8UC1 8位无符号整数从0到255
- CV_8SC1 8位有符号整数从-128到127
- CV_16UC1 16位无符号整数从0到65536
- CV_16SC1 16位有符号整数从-32768到32767
- CV_32SC1 32位有符号整数从-2^(32-1)到2^(32-1)-1
- CV_32FC1 32位有符号小数
- CV_64FC1 64位有符号小数
你可以使用这样的方式来创建一幅图片:
Mat image = new Mat(new Size(3,4), CvType.CV_8UC3, new Scalar(new double[]{128,3,4}));
Mat有很多中构造方法,这里只是其中一种,这样构造的mat对象含义是宽度为3个像素,高度为4个像素,8位无符号三通道图像,第一通道也就是Red全为128,第二通道Green全为3,第三通道Blue全是4,我们可以使用如下的代码打印出这个对象的信息以及每个像素点的数据。
Mat image = new Mat(new Size(3, 4), CvType.CV_8UC3, new Scalar(new double[]{128, 3, 4})); System.out.println(image + " width=" + image.width() + " height=" + image.height()); System.out.println(image.dump());
直接使用Mat对象的toString()方法,将会打印出mat对象的基本信息,dump()返回每个像素点的值,注意当像素宽高太大时,可能耗时比较长。输出结果:
Mat [ 4*3*CV_8UC3, isCont=true, isSubmat=false, nativeObj=0x50aaf0, dataAddr=0x4dce00 ] width=3 height=4 [128, 3, 4, 128, 3, 4, 128, 3, 4; 128, 3, 4, 128, 3, 4, 128, 3, 4; 128, 3, 4, 128, 3, 4, 128, 3, 4; 128, 3, 4, 128, 3, 4, 128, 3, 4]
由于图像太小,显示出来也是特别小,基本看不出来,可以把宽高设置大一些然后显示,就是一片深蓝色,也就是red=128,green=3,blue=4的颜色。
一些需要注意的点:
java中没有无符号这个说法,所有的基本数据类型都是有符号的,比如对于CV_8UC1来说,需要表示的像素是从0到255,在C++中只需要一个byte就够了,但是java中确不能用一个字节来表示0到255,所以是只能向上类型转换了。我们先来看一段代码:
Mat image = new Mat(new Size(3, 4), CvType.CV_8UC1, new Scalar(new double[]{234})); image.put(1, 2, new byte[]{123}); System.out.println(image); System.out.println(image.dump()); image.put(1, 2, new byte[]{200 - 256}); System.out.println(image.dump());
这段代码先创建了一个3*4的8位单通道无符号的图像,每个像素点都是234,然后将[1,2]这个位置(注意从[0,0]坐标点开始计数)值置为123,然后打印出来像素,没问题,123<127所以可以put成功,但是如果我们想改一个大于127的值呢,byte数据会直接报错,编译都无法通过,那是不是就无法创建一个大于127的值呢,当然不是,将大于127的值减去一个256,比如现在我想设置成200,怎么做呢,直接用200-256,也就是存一个-56,然后再打印出来看看是不是可以。结果当然是可以的,200已经设置成功了。那如果是一个CV_8SC1的图像呢,该是多少直接设置成多少就可以了。为什么能打印出200呢,不是byte值得吗?不妨看看image.get(1,2)这个方法,这个方法返回了一个double[]类型,当时能保存200了,而dump返回的是String,在内部已经将-56转成了200的字符串.
所以对于获得一幅图像的像素点的时候不用考虑太多,但是如果你要设置某个像素点,并且这个像素点的数据类型或是位宽小于你要设置的值得位宽时,就得注意了,不过也有更安全的做法,那就是全都使用double设置,在内部会自动转换。后面会看到在java版本的OpenCV的方法中很多地方都是使用的是double,这个确实是浪费了很多内存,很多时候我们处理的都是8位无符号的3通道彩色图像,映射到java程序中,如果要把这个图像的数据dump过来,那就得8倍的内存来保存(C++中一个字节就够但java就需要用double表示,double占了8位)。
下一遍开始就正式要开始介绍使用OpenCV来处理图像了。