# 第三章 第一个OpenCV的JavaFX应用程序

---

***注意***:我们假设您现在已经阅读了之前的教程。如果没有,请在[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应用程序

  本教程将指导你使用Eclipse中的OpenCV库来创建一个简单的JavaFX GUI应用程序。

## 3.2 在本教程中要做什么

  在本教程中,我们将:

    •安装**e(fx)clipse**插件和 _Scene Builder_ (*Scene Builder*安装不做硬性要求);

    •使用 _Scene Builder_ ;

    •编写并运行应用程序。

## 3.3 JavaFX中的第一个应用程序

  本教程编写出来的应用程序将捕获来自网络摄像机的视频流并将其显示在图形用户界面(GUI)上。

  我们是使用Scene Builder来创建GUI的。创建完毕后,GUI将具有一个按钮和一个简单的图像视图框,前者用于播放/关闭视频流,后者用于放置视频流帧。

## 3.4 安装e(fx)clipse插件和Scene Builder

  请按照本教程的指导在Eclipse中安装**e(fx)clipse**插件,本教程内容详见[http://www.eclipse.org/efxclipse/install.html#fortheambitious](http://www.eclipse.org/efxclipse/install.html#fortheambitious)。

  如果不想安装此类插件,只需创建一个惯用的**Java项目**——*JavaFX Scene Builder 2.0*即可。请从[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*。

  现在可以来新建一个JavaFX项目了,具体操作为:

    •转到“File > New > Project...”,选择“JavaFX project....”;

![U8Puid.png](https://images.gitee.com/uploads/images/2020/0716/170447_07d86fe4_1464254.png)

    •输入项目名称(名称自选),单击“Next”;

![U8P7wD.png](https://images.gitee.com/uploads/images/2020/0716/170455_2965d862_1464254.png)

    •添加OpenCV用户库到刚刚创建的项目,单击“Next”;

![U8iv4J.png](https://images.gitee.com/uploads/images/2020/0716/170447_73b87349_1464254.png)

    •分别选定Package名称、*FXML文件*名称以及*控制器类*名称。关于你创建的GUI的描述将以FXML语言的形式包含在FXML文件中。而GUI组件与用户交互时所必须调用、管理的方法和事件都将由*控制器类*处理。

## 3.5 使用Scene Builder

  如果之前已经安装过*Scene Builder*,就可以直接在Eclipse中右键单击FXML文件然后选择“Open with SceneBuilder”。

   _Scene Builder_ 是通过与图形界面交互来帮助构建你的GUI的。这样一来,你就可以实时预览你的窗口效果。此外,只需编辑图形预览即可实现GUI组件内容的修改以及位置的调整。

  不妨来具体地看一看我到底在讲些什么。

  *FXML文件*最开始只有一个*AnchorPane*。AnchorPane中,允许存在一个偏离距离,位于AnchorPane子节点边缘锚定的位置与AnchorPane自身边缘所在的位置之间。如果AnchorPane设置了border和(或)padding,则偏离距离要从它们的内边缘开始算起。对于受到AnchorPane管理的子节点,无论它们是否可见,都将被AnchorPane一一展开。而非托管子节点则不会被AnchorPane展开。

  当然,你也可以删除AnchorPane并添加BorderPane。BorderPane在TOP、LEFT、RIGHT、BOTTOM以及CENTER这5个固定位置展开子节点。

![U8k6w8.png](https://images.gitee.com/uploads/images/2020/0716/170450_ecd69550_1464254.png)

  从“Container”菜单中拖拽一个BorderPane并将其放到“Hierarchy”菜单中,即可添加一个BorderPane。BorderPane添加完毕后,我们再来添加一个Button,它之后可以用来播放/关闭视频流。从“Contols”菜单中拖拽一个Button并将其放到我们刚刚添加的BP(即BorderPane)的**BOTTOM**区域,Button就添加完成了。

  我们现在可以看到,界面的右方区域有3个菜单(“Properties”、“Layout”、“Code”),用于自定义被选中组件。例如,我们可以在“Properties”菜单下的“Text”区域修改之前添加的Button内容为“StartCamera”,还可以在“Code”菜单下的“fx:id”区域修改Button的id(比如改成“start_btn”)。接下来在从**控制器**方法编辑button属性时,我们就需要用到button的id。

  在界面中还可以看到,button现在离窗口的距离特别近。因此要为button设置一定的下边距。我们可以在“Layout”菜单中进行相应操作。

![U8kzOx.png](https://images.gitee.com/uploads/images/2020/0716/170449_80184d49_1464254.png)

![U8AEpd.png](https://images.gitee.com/uploads/images/2020/0716/170448_d93bfe99_1464254.png)

  为了能让button正常运行,我们必须在“Code”菜单下的“OnAction”这一栏设定好方法名称(比如“startCamera”),此处设定的方法将执行我们预想的功能。

![U8EPuq.png](https://images.gitee.com/uploads/images/2020/0716/170449_b1f17c57_1464254.png)

  然后从“Controls”菜单中拖拽一个*ImageView*添加到BP的**CENTER**区域中。同样地,为ImageView设置一定的边距并对其id加以修改(比如改成“currentFrame”)。

![U8EzdK.png](https://images.gitee.com/uploads/images/2020/0716/170450_2dc463fe_1464254.png)

  最后,必须指定一个控制器类去管理我们的GUI。为此,需添加我们之前选定的控制器类名称到位于窗口左下方的“Controller”菜单下的“Controller class”这一栏。

  我们刚刚使用Scene Builder创建了我们第一个GUI。如果你现在保存文件回到Eclipse,你会看到Eclipse已经自动生成了某些FXML代码。

## 3.6 JavaFX中的一些重要概念

  **Stage**:应用程序显示的地方(比如Windows窗口);

  **Scene**:即节点容器,此容器中的节点构成了你的应用程序的某个“页面”;

  **Node**:Scene中的一个元素,具有可视化、可交互的特点。节点可能是以分层嵌套的形式存在于Scene中。

  在*Main类*中,我们需要将自身的*primary stage*传递给*start*函数:

```

public void start(Stage primaryStage)

```

  也是在*Main类*中,我们还要载入FXML文件,该文件将会填充我们的Stage、Scene的*root element*以及控制器类:

```

FXMLLoader loader = new FXMLLoader(getClass().getResource("FXHelloCV.fxml"));

BorderPane root = (BorderPane) loader.load();

FXController controller = loader.getController();

```

## 3.7 使用控制器类管理GUI交互

  关于我们的JavaFX应用程序,我们需要做两件最基本的事:一是控制按下Button;二是控制刷新ImageView。为了实现上述操作,我们需要在GUI组件和控制器类变量间创建一个引用:

```

@FXML

private Button button;

@FXML

private ImageView currentFrame;

```

  **@FXML**标签用于链接变量到FXML文件中的某个元素,并且该变量值应等于相链接的元素的id。

  **@FXML**标签用于某个元素设定的方法时,功能同上。例如:

    对于:

```

```

    我们设置:

```

@FXML

protected void startCamera(ActionEvent event) { ...

```

## 3.8 视频捕获

  VideoCapture类基本上已经整合了视频处理所需要的一切函数。这是建立在FFmpeg开源库的基础上的。

```

private VideoCapture capture = new VideoCapture();

```

  一段视频由若干连续的图像组成,其中的这些图像我们称之为帧。视频文件中,用帧率来明确两帧之间的间隔时长。而对于摄像机来说,每秒可数字化的帧数通常是有限制的。在这里,我们设置帧率为30帧/秒。为此我们需要初始化一个计时器(即`ScheduledExecutorService`),该计时器每33毫秒将会启动一次后台任务。

```

Runnable frameGrabber = new Runnable() { ... }

this.timer = Executors.newSingleThreadScheduledExecutor();

this.timer.scheduleAtFixedRate(frameGrabber, 0, 33, TimeUnit.

˓→ MILLISECONDS);

```

  要检查VideoCapture类是否成功绑定了视频源的话,我们需要使用**isOpened**函数:

```

if (this.capture.isOpened()) { ... }

```

  视频在其析构函数被调用时就会自动关闭。可是如果你想在此之前就关闭视频的话,就需要调用其释放函数了:

```

this.capture.release();

```

  鉴于视频帧纯粹就是图像而已,所以我们只需要将它们从VideoCapture对象中提取出来再放入Mat 1即可。

```

Mat frame = new Mat();

```

  从“read”或“overloaded >> operator”读取帧时,由于视频流都是连续的,所以帧可能是顺次而出。

```

this.capture.read(frame);

```

  我们还需要将我们的图像从*BGR*格式转换成*灰度*图。OpenCV中有一个非常好的函数可以完成此类转换:

```

Imgproc.cvtColor(frame, frame, Imgproc.COLOR_BGR2GRAY);

```

  **从上面这行代码可以看到,cvtColor用到以下参数:**

      •**源图像(frame);**

      •**目标图像(frame),用来保存转换后的灰度图;**

      •**附加参数,用于指示将要执行何种转换。在这里使用的附加参数是COLOR_BGR2GRAY (不使用imread是因为对于彩色图像imread默认使用BGR通道顺序)。**

  为了能将捕获的帧放入ImageView中,我们需要将矩阵转换成图像。具体操作如下:

    •首先,我们创建一个缓冲区,用于存储矩阵。

```

MatOfByte buffer = new MatOfByte();

```

    •然后,使用**imencode**函数,将捕获的帧放入缓冲区,下列这行代码便会将图像编码到内存缓冲区中:

```

Imgcodecs.imencode(".png", frame, buffer);

```

    **imencode**函数压缩图像并存储到内存缓冲区中,内存缓冲区会自适应压缩后图像的大小。

---

**注意:** **imencode**函数返回**CV_8UC1**型的单行矩阵,被编码成字节数组的图像就包含在返回的矩阵中。

---

    **imencode函数用到3个参数:**

      •**“.png”,即文件扩展名,用于定义输出格式;**

      **•frame,即待写入图像;**

      **•buffer,即输出缓存区,可以自适应压缩后图像的大小。**

  将捕获的帧放入缓存区后,我们就要用到**ByteArrayInputStream**来流式传输缓存区中被压缩的图像:

```

new Image(new ByteArrayInputStream(buffer.toArray()));

```

  通过流式传输得到新的图像后,我们就可以将其放到ImageView中。不过使用Java 1.8版本时,我们是无法在与主线程不同的线程中更新GUI的元素的。因此我们需要先从其他的线程中获取新帧,然后在主线程中刷新我们的ImageView:

```

Image imageToShow = grabFrame();

Platform.runLater(new Runnable() {

@Override public void run() { currentFrame.setImage(imageToShow); }

});

```

![U8u2zF.png](https://images.gitee.com/uploads/images/2020/0716/170452_e1829e8e_1464254.png)

一键复制

编辑

Web IDE

原始数据

按行查看

历史

)类 新建javafx程序时_第三章 第一个OpenCV的JavaFX应用程序.md相关推荐

  1. java程序 购物车_用java代码写一个简单的网上购物车程序

    1 需求:1.写一个商品类,有商品编号.商品名称.商品分类.商品单价属性.2.写一个商品条目信息类,有商品和数量两个属性,有商品总价格方法.2 3 3.写一个购物车类,有添加商品方法.查看订单信息,删 ...

  2. OpenCV函数简记_第三章数字图像的滤波处理(方框,均值,高斯,中值和双边滤波)

    系列文章目录 OpenCV函数简记_第一章数字图像的基本概念(邻域,连通,色彩空间) OpenCV函数简记_第二章数字图像的基本操作(图像读写,图像像素获取,图像ROI获取,图像混合,图形绘制) Op ...

  3. 《精通数据仓库设计》中英对照_第三章

    <精通数据仓库设计>中英对照_第三章 第二部分 模型开发 数据仓库应该表示企业数据的各个方面,这些方面以主题域和业务数据模型开始.我们将在第3章使用一个假想的公司,指导一步一步地开发这两个 ...

  4. c语言中,x-y,'105',ab,7f8那个是正确的,C语言程序设计_第三章 数据.ppt

    C语言程序设计_第三章 数据 * 运算符功能 与运算量关系 要求运算量个数 要求运算量类型 运算符优先级别 结合方向 结果的类型 学习运算符应注意 * 基本算术运算符: + - * / % 结合方向: ...

  5. 软考 程序员教程-第三章 数据库基础知识

    软考 程序员教程-第三章 数据库基础知识 第三章 数据库基础知识 3.1 基本概念 数据库系统(DataBase System,DBS)由数据库(DataBase,DB).硬件.软件和人员4大部分组成 ...

  6. ArcGIS for Desktop入门教程_第三章_Desktop软件安装 - ArcGIS知乎-新一代ArcGIS问答社区...

    原文:ArcGIS for Desktop入门教程_第三章_Desktop软件安装 - ArcGIS知乎-新一代ArcGIS问答社区 1 软件安装 1.1 安装前准备 请确认已经收到来自Esri中国( ...

  7. Lync Server 2010的部署系列_第三章 证书、架构、DNS规划

    Lync Server 2010的部署系列_第三章 证书.架构.DNS规划 一.证书规划 组件 使用者名称 使用者备用名称条目/顺序 证书颁发机构 (CA) 备注 边缘外部接口 Sip.Giantha ...

  8. 管理系统中计算机应用课件,管理系统中计算机应用_第三章课件.ppt

    <管理系统中计算机应用_第三章课件.ppt>由会员分享,提供在线免费全文阅读可下载,此文档格式为ppt,更多相关<管理系统中计算机应用_第三章课件.ppt>文档请在天天文库搜索 ...

  9. 为什么3年的Java高级程序员薪水仅仅8k-10k,而一个Linux底层C语言程序员两年经验就敢要1...

    为什么80%的码农都做不了架构师?>>>    为什么3年的Java高级程序员薪水仅仅8k-10k,而一个Linux底层C语言程序员两年经验就敢要10k的薪水?   由于目前国内嵌入 ...

最新文章

  1. tp5.0 新增模块
  2. 征战蓝桥 —— 2016年第七届 —— C/C++A组第2题——生日蜡烛
  3. C语言程序设计 | 动态内存管理:动态内存函数介绍,常见的动态内存错误,柔性数组
  4. Flink表转流一例+何时应该使用execute()
  5. horizon服务主要模块_Horizon Workspace 快速部署指南三(配置Workspace数据模块)
  6. Nacos(三)之架构
  7. AlphaGo背后这项核心技术,后来怎么样了?
  8. PHP程序员如何突破成长瓶颈(php开发三到四年)
  9. ubb php论坛程序,论坛UBB代码 推荐
  10. 计算机主板设置中的英语,技嘉主板bios设置(进BIOS按什么键+中英文对照设置教程)...
  11. 固态硬盘和机械硬盘的区别(7大区别,简单易懂)
  12. python视频字幕处理_用Python处理字幕文件
  13. 翻译:俄国卫星GLONASS 简介 天基全球导航卫星系统 (GNSS)
  14. 写了placement new也要写placement delete——条款52
  15. 利用python计算每个月有多少天和在两个日期中按月遍历
  16. 机器学习中Batch Size、Iteration和Epoch的概念
  17. 程序员职场务必了解的薪资待遇
  18. spec cpu 2017使用教程
  19. 佳明 Fenix 7S、佳明Fenix 7和Fenix 7X评测
  20. 立体字3D字体数字设计|造型艺术字,灵感来源,速码!

热门文章

  1. Linux搭建高并发高可用Redis集群
  2. python中的匿名函数lambda
  3. 洛克王国进不去 不显示服务器,《洛克王国》FAQ:注册和登陆问题
  4. android 视频转字节,如何将视频文件(.mp4)格式转换为android中的二进制格式?...
  5. 为什么搜索与推荐场景用AUC评价模型好坏?
  6. 震惊!丧心病狂的夕小瑶推出新一轮写作计划!
  7. Java远程通讯技术及原理分析
  8. 大数据技术和python开发工程师
  9. 论文笔记(A Neural Influence Diffusion Model for Social Recommendation)
  10. PersonGraphDataSet近十万的开放人物关系图谱项目