)类 新建javafx程序时_第三章 第一个OpenCV的JavaFX应用程序.md
# 第三章 第一個(gè)OpenCV的JavaFX應(yīng)用程序
---
***注意***:我們假設(shè)您現(xiàn)在已經(jīng)閱讀了之前的教程。如果沒(méi)有,請(qǐng)?jiān)赱http://opencv-java-tutorials.readthedocs.org/en/latest/index.html](http://opencv-java-tutorials.readthedocs.org/en/latest/index.html)查看。您還可以在
[https://github.com/opencv-java/](https://github.com/opencv-java/)找到源代碼和資源。
---
## 3.1 OpenCV的JavaFX應(yīng)用程序
??本教程將指導(dǎo)你使用Eclipse中的OpenCV庫(kù)來(lái)創(chuàng)建一個(gè)簡(jiǎn)單的JavaFX GUI應(yīng)用程序。
## 3.2 在本教程中要做什么
??在本教程中,我們將:
?????安裝**e(fx)clipse**插件和 _Scene Builder_ (*Scene Builder*安裝不做硬性要求);
?????使用 _Scene Builder_ ;
?????編寫(xiě)并運(yùn)行應(yīng)用程序。
## 3.3 JavaFX中的第一個(gè)應(yīng)用程序
??本教程編寫(xiě)出來(lái)的應(yīng)用程序?qū)⒉东@來(lái)自網(wǎng)絡(luò)攝像機(jī)的視頻流并將其顯示在圖形用戶(hù)界面(GUI)上。
??我們是使用Scene Builder來(lái)創(chuàng)建GUI的。創(chuàng)建完畢后,GUI將具有一個(gè)按鈕和一個(gè)簡(jiǎn)單的圖像視圖框,前者用于播放/關(guān)閉視頻流,后者用于放置視頻流幀。
## 3.4 安裝e(fx)clipse插件和Scene Builder
??請(qǐng)按照本教程的指導(dǎo)在Eclipse中安裝**e(fx)clipse**插件,本教程內(nèi)容詳見(jiàn)[http://www.eclipse.org/efxclipse/install.html#fortheambitious](http://www.eclipse.org/efxclipse/install.html#fortheambitious)。
??如果不想安裝此類(lèi)插件,只需創(chuàng)建一個(gè)慣用的**Java項(xiàng)目**——*JavaFX Scene Builder 2.0*即可。請(qǐng)從[http://www.oracle.com/technetwork/java/javafxscenebuilder-1x-archive-2199384.html](http://www.oracle.com/technetwork/java/javafxscenebuilder-1x-archive-2199384.html)下載安裝*JavaFX Scene Builder 2.0*。
??現(xiàn)在可以來(lái)新建一個(gè)JavaFX項(xiàng)目了,具體操作為:
?????轉(zhuǎn)到“File > New > Project...”,選擇“JavaFX project....”;

?????輸入項(xiàng)目名稱(chēng)(名稱(chēng)自選),單擊“Next”;

?????添加OpenCV用戶(hù)庫(kù)到剛剛創(chuàng)建的項(xiàng)目,單擊“Next”;

?????分別選定Package名稱(chēng)、*FXML文件*名稱(chēng)以及*控制器類(lèi)*名稱(chēng)。關(guān)于你創(chuàng)建的GUI的描述將以FXML語(yǔ)言的形式包含在FXML文件中。而GUI組件與用戶(hù)交互時(shí)所必須調(diào)用、管理的方法和事件都將由*控制器類(lèi)*處理。
## 3.5 使用Scene Builder
??如果之前已經(jīng)安裝過(guò)*Scene Builder*,就可以直接在Eclipse中右鍵單擊FXML文件然后選擇“Open with SceneBuilder”。
?? _Scene Builder_ 是通過(guò)與圖形界面交互來(lái)幫助構(gòu)建你的GUI的。這樣一來(lái),你就可以實(shí)時(shí)預(yù)覽你的窗口效果。此外,只需編輯圖形預(yù)覽即可實(shí)現(xiàn)GUI組件內(nèi)容的修改以及位置的調(diào)整。
??不妨來(lái)具體地看一看我到底在講些什么。
??*FXML文件*最開(kāi)始只有一個(gè)*AnchorPane*。AnchorPane中,允許存在一個(gè)偏離距離,位于AnchorPane子節(jié)點(diǎn)邊緣錨定的位置與AnchorPane自身邊緣所在的位置之間。如果AnchorPane設(shè)置了border和(或)padding,則偏離距離要從它們的內(nèi)邊緣開(kāi)始算起。對(duì)于受到AnchorPane管理的子節(jié)點(diǎn),無(wú)論它們是否可見(jiàn),都將被AnchorPane一一展開(kāi)。而非托管子節(jié)點(diǎn)則不會(huì)被AnchorPane展開(kāi)。
??當(dāng)然,你也可以刪除AnchorPane并添加BorderPane。BorderPane在TOP、LEFT、RIGHT、BOTTOM以及CENTER這5個(gè)固定位置展開(kāi)子節(jié)點(diǎn)。

??從“Container”菜單中拖拽一個(gè)BorderPane并將其放到“Hierarchy”菜單中,即可添加一個(gè)BorderPane。BorderPane添加完畢后,我們?cè)賮?lái)添加一個(gè)Button,它之后可以用來(lái)播放/關(guān)閉視頻流。從“Contols”菜單中拖拽一個(gè)Button并將其放到我們剛剛添加的BP(即BorderPane)的**BOTTOM**區(qū)域,Button就添加完成了。
??我們現(xiàn)在可以看到,界面的右方區(qū)域有3個(gè)菜單(“Properties”、“Layout”、“Code”),用于自定義被選中組件。例如,我們可以在“Properties”菜單下的“Text”區(qū)域修改之前添加的Button內(nèi)容為“StartCamera”,還可以在“Code”菜單下的“fx:id”區(qū)域修改Button的id(比如改成“start_btn”)。接下來(lái)在從**控制器**方法編輯button屬性時(shí),我們就需要用到button的id。
??在界面中還可以看到,button現(xiàn)在離窗口的距離特別近。因此要為button設(shè)置一定的下邊距。我們可以在“Layout”菜單中進(jìn)行相應(yīng)操作。


??為了能讓button正常運(yùn)行,我們必須在“Code”菜單下的“OnAction”這一欄設(shè)定好方法名稱(chēng)(比如“startCamera”),此處設(shè)定的方法將執(zhí)行我們預(yù)想的功能。

??然后從“Controls”菜單中拖拽一個(gè)*ImageView*添加到BP的**CENTER**區(qū)域中。同樣地,為ImageView設(shè)置一定的邊距并對(duì)其id加以修改(比如改成“currentFrame”)。

??最后,必須指定一個(gè)控制器類(lèi)去管理我們的GUI。為此,需添加我們之前選定的控制器類(lèi)名稱(chēng)到位于窗口左下方的“Controller”菜單下的“Controller class”這一欄。
??我們剛剛使用Scene Builder創(chuàng)建了我們第一個(gè)GUI。如果你現(xiàn)在保存文件回到Eclipse,你會(huì)看到Eclipse已經(jīng)自動(dòng)生成了某些FXML代碼。
## 3.6 JavaFX中的一些重要概念
??**Stage**:應(yīng)用程序顯示的地方(比如Windows窗口);
??**Scene**:即節(jié)點(diǎn)容器,此容器中的節(jié)點(diǎn)構(gòu)成了你的應(yīng)用程序的某個(gè)“頁(yè)面”;
??**Node**:Scene中的一個(gè)元素,具有可視化、可交互的特點(diǎn)。節(jié)點(diǎn)可能是以分層嵌套的形式存在于Scene中。
??在*Main類(lèi)*中,我們需要將自身的*primary stage*傳遞給*start*函數(shù):
```
public void start(Stage primaryStage)
```
??也是在*Main類(lèi)*中,我們還要載入FXML文件,該文件將會(huì)填充我們的Stage、Scene的*root element*以及控制器類(lèi):
```
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXHelloCV.fxml"));
BorderPane root = (BorderPane) loader.load();
FXController controller = loader.getController();
```
## 3.7 使用控制器類(lèi)管理GUI交互
??關(guān)于我們的JavaFX應(yīng)用程序,我們需要做兩件最基本的事:一是控制按下Button;二是控制刷新ImageView。為了實(shí)現(xiàn)上述操作,我們需要在GUI組件和控制器類(lèi)變量間創(chuàng)建一個(gè)引用:
```
@FXML
private Button button;
@FXML
private ImageView currentFrame;
```
??**@FXML**標(biāo)簽用于鏈接變量到FXML文件中的某個(gè)元素,并且該變量值應(yīng)等于相鏈接的元素的id。
??**@FXML**標(biāo)簽用于某個(gè)元素設(shè)定的方法時(shí),功能同上。例如:
????對(duì)于:
```
```
????我們?cè)O(shè)置:
```
@FXML
protected void startCamera(ActionEvent event) { ...
```
## 3.8 視頻捕獲
??VideoCapture類(lèi)基本上已經(jīng)整合了視頻處理所需要的一切函數(shù)。這是建立在FFmpeg開(kāi)源庫(kù)的基礎(chǔ)上的。
```
private VideoCapture capture = new VideoCapture();
```
??一段視頻由若干連續(xù)的圖像組成,其中的這些圖像我們稱(chēng)之為幀。視頻文件中,用幀率來(lái)明確兩幀之間的間隔時(shí)長(zhǎng)。而對(duì)于攝像機(jī)來(lái)說(shuō),每秒可數(shù)字化的幀數(shù)通常是有限制的。在這里,我們?cè)O(shè)置幀率為30幀/秒。為此我們需要初始化一個(gè)計(jì)時(shí)器(即`ScheduledExecutorService`),該計(jì)時(shí)器每33毫秒將會(huì)啟動(dòng)一次后臺(tái)任務(wù)。
```
Runnable frameGrabber = new Runnable() { ... }
this.timer = Executors.newSingleThreadScheduledExecutor();
this.timer.scheduleAtFixedRate(frameGrabber, 0, 33, TimeUnit.
?→ MILLISECONDS);
```
??要檢查VideoCapture類(lèi)是否成功綁定了視頻源的話(huà),我們需要使用**isOpened**函數(shù):
```
if (this.capture.isOpened()) { ... }
```
??視頻在其析構(gòu)函數(shù)被調(diào)用時(shí)就會(huì)自動(dòng)關(guān)閉。可是如果你想在此之前就關(guān)閉視頻的話(huà),就需要調(diào)用其釋放函數(shù)了:
```
this.capture.release();
```
??鑒于視頻幀純粹就是圖像而已,所以我們只需要將它們從VideoCapture對(duì)象中提取出來(lái)再放入Mat 1即可。
```
Mat frame = new Mat();
```
??從“read”或“overloaded >> operator”讀取幀時(shí),由于視頻流都是連續(xù)的,所以幀可能是順次而出。
```
this.capture.read(frame);
```
??我們還需要將我們的圖像從*BGR*格式轉(zhuǎn)換成*灰度*圖。OpenCV中有一個(gè)非常好的函數(shù)可以完成此類(lèi)轉(zhuǎn)換:
```
Imgproc.cvtColor(frame, frame, Imgproc.COLOR_BGR2GRAY);
```
??**從上面這行代碼可以看到,cvtColor用到以下參數(shù):**
???????**源圖像(frame);**
???????**目標(biāo)圖像(frame),用來(lái)保存轉(zhuǎn)換后的灰度圖;**
???????**附加參數(shù),用于指示將要執(zhí)行何種轉(zhuǎn)換。在這里使用的附加參數(shù)是COLOR_BGR2GRAY (不使用imread是因?yàn)閷?duì)于彩色圖像imread默認(rèn)使用BGR通道順序)。**
??為了能將捕獲的幀放入ImageView中,我們需要將矩陣轉(zhuǎn)換成圖像。具體操作如下:
?????首先,我們創(chuàng)建一個(gè)緩沖區(qū),用于存儲(chǔ)矩陣。
```
MatOfByte buffer = new MatOfByte();
```
?????然后,使用**imencode**函數(shù),將捕獲的幀放入緩沖區(qū),下列這行代碼便會(huì)將圖像編碼到內(nèi)存緩沖區(qū)中:
```
Imgcodecs.imencode(".png", frame, buffer);
```
????**imencode**函數(shù)壓縮圖像并存儲(chǔ)到內(nèi)存緩沖區(qū)中,內(nèi)存緩沖區(qū)會(huì)自適應(yīng)壓縮后圖像的大小。
---
**注意:** **imencode**函數(shù)返回**CV_8UC1**型的單行矩陣,被編碼成字節(jié)數(shù)組的圖像就包含在返回的矩陣中。
---
????**imencode函數(shù)用到3個(gè)參數(shù):**
???????**“.png”,即文件擴(kuò)展名,用于定義輸出格式;**
??????**?frame,即待寫(xiě)入圖像;**
??????**?buffer,即輸出緩存區(qū),可以自適應(yīng)壓縮后圖像的大小。**
??將捕獲的幀放入緩存區(qū)后,我們就要用到**ByteArrayInputStream**來(lái)流式傳輸緩存區(qū)中被壓縮的圖像:
```
new Image(new ByteArrayInputStream(buffer.toArray()));
```
??通過(guò)流式傳輸?shù)玫叫碌膱D像后,我們就可以將其放到ImageView中。不過(guò)使用Java 1.8版本時(shí),我們是無(wú)法在與主線程不同的線程中更新GUI的元素的。因此我們需要先從其他的線程中獲取新幀,然后在主線程中刷新我們的ImageView:
```
Image imageToShow = grabFrame();
Platform.runLater(new Runnable() {
@Override public void run() { currentFrame.setImage(imageToShow); }
});
```

一鍵復(fù)制
編輯
Web IDE
原始數(shù)據(jù)
按行查看
歷史
總結(jié)
以上是生活随笔為你收集整理的)类 新建javafx程序时_第三章 第一个OpenCV的JavaFX应用程序.md的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java的比较运算符是_Java 基础(
- 下一篇: 删除当前文件夹下特定名称文件