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來處理圖像了。