带你读《Java图像处理:基于OpenCV与JVM》之一:基于JavaVM的OpenCV

丰富的线上&线下活动,深入探索云世界

做任务,得社区积分和周边

最真实的开发者用云体验

让每位学生受益于普惠算力

让创作激发创新

资深技术专家手把手带教

遇见技术追梦人

技术交流,直击现场

海量开发者使用工具、手册,免费下载

极速、全面、稳定、安全的开源镜像

开发手册、白皮书、案例集等实战精华

为开发者定制的Chrome浏览器插件

华章程序员书库

Java图像处理:基于OpenCV与JVMJavaImageProcessingRecipes:WithOpenCVandJVM

一个目录,两个文件。为了简化问题,第一个Java例子十分简单。

接下来,让我们来看一看project.clj文件中的细节:

这其实是Clojure代码,不过我们把它看作一种领域专用语言(DomainSpecificLanguage,DSL),即使用特定术语描述项目的语言。为了方便,每种术语在表1-1中有解释。

接下来,请创建对应的文件夹和文件目录,复制并粘贴每个文件对应的内容。完成之后,就可以运行你的第一个Leiningen命令:

这个命令会根据你的环境,在你的终端或控制台中生成以下内容:

太棒啦!我们的旅程开始啦!但是,等一等,刚才到底发生了什么呢?其中包含了一点魔法。Leiningen的run命令会让Leiningen运行一个编译过的Java类main函数。这个被运行的类定义在项目的元数据中,你应该记得,叫作Hello。在运行Java类之前,我们需要先编译它。默认情况下,Leiningen会在运行run之前进行编译,这也解释了“Compiling...”是从哪里来的。之后,你可能会注意到,在你的项目中创建了一个叫作target的文件夹,里面包含了一个叫作classes的文件夹和一个Hello.class文件。

target和classes文件夹是编译过的Java字节码(bytecode)的默认存储地址,这个target文件夹之后会被加入Java运行时的类路径(classpath)。紧跟着是“leinrun”触发的执行阶段,Hello类的主函数中的代码块被执行,并输出信息。

你可能会问:“如果我有多个Java文件,并且想运行非主函数之外的其他函数呢?”这是一个非常合理的问题,在第1章中编写和运行不同代码时,你会经常使用这个技巧。假如你在同样的Java文件夹中写了第二个Java类,叫作Hello2.Java,其中包含更新的旅途内容。

为了运行Hello2.java的主函数,在调用leinrun时,需要加上-m选项,这里m代表着主函数,之后跟着需要运行的Java类的名称。

这个命令输出以下内容:

太好啦!根据这些指导,你可以勇往直前,运行你的第一个OpenCVJava程序了。

问题定义在通过Leiningen设置的Java项目中,直接使用OpenCV库。在利用OpenCV运行Java代码之前,有一个令人头疼的问题(自己编译OpenCV封装时),希望这一步能够越简单越好。解决方法1.1节介绍了Leiningen的基本环境配置。这一节介绍如何添加对OpenCVC++库和Java封装的依赖。工作原理第一个OpenCV例子将使用Leiningen项目模板来配置,其中project.clj文件和文件夹已经创建完毕。Leiningen项目模板不需要单独下载,可以通过在创建新项目时使用Leiningen的集成命令new来调用。为了在你的机器上创建该项目,在命令行中运行lein命令。无论是在Windows还是Mac中,这个命令都会输出:

这个看起来好像不是很令人印象深刻,但是这些文件和上一个攻略中的两个文件基本一样,都是一个项目描述文件和一个Java文件。project.clj文件的内容与之前有所不同:

这段代码做了什么呢?①它告诉Java运行时利用loadLibrary来加载nativeopencv库。这是使用OpenCV的必要步骤,每次运行你的应用时都要调用一次。②通过Java对象,创建了一个Mat对象。Mat本质上是一个图像存储器,像矩阵一样,这里我们设置它的尺寸为3×3:高度为3个像素,宽度为3个像素。每个像素的类型是8UC1,这个奇怪的名字代表着包含8个位的无符号(8U)单通道(C1)整数。③最终输出Mat(矩阵)对象中的内容。和之前一样,这个项目是可以直接被运行的,无论你使用什么平台,leinrun命令都可以完成任务。

这里的1和0代表着创建的矩阵对象的实际内容。

问题定义虽然lein命令非常通用,你可能还是想在后台启动你的程序,并且在更新代码的时候让你的代码自动运行。解决方法Leiningen配有自动插件。启用后,该插件会监视文件模式的变化并触发命令。让我们来试试吧!工作原理当你用jvm-opencv模板创建项目时(请参阅1.2节),你会注意到project.clj文件的内容略长于本书中显示的内容。它实际上看起来更像这样:

多出来的两行被高亮显示出来。一行是项目元数据在:plugins部分增加了lein-auto的插件。另一行(即:auto部分)定义要监视变化的文件模式,这里所有以Java结尾的文件的变化都会激活自动刷新的子命令。回到命令行,现在我们将在通常的run命令前添加auto命令,你需要编写下面这样的命令:

第一次运行它时,它将提供与之前相同的输出,但是会添加一些额外的行:

不错,请注意,Leiningen命令尚未完成运行,它实际上是在监听文件的变化。从现在开始,你可以随意修改HelloCv的Java代码中Mat对象的大小。将以下行

替换为

更新的代码表示Mat对象现在是5×5矩阵,每个像素仍然由一个字节的整数表示。然后查看Leiningen命令所在的终端或控制台,你会看到以下正在更新的输出:

在底部打开的终端会让你输入相同的"leinautorun"命令,因此你不需要额外的命令提示符或者另外的终端窗口来执行Leiningen的自动运行函数。这样你将能让所有的代码都在一个窗口中编写。理想情况下,Atom布局看起来如图1-4或图1-5所示。

最后,对图像进行的更新,虽然不能被实时地显示出来,但在保存文件时可以看到。如果你在后台打开文件,会看到文件在每次保存都会被刷新,保存是通过OpenCV的imwrite函数完成的。所以,由于有leiningenautorun在后台一直运行,保存文件时,compilation/run循环会被触发并更新图像。图1-7显示了即使没有保存文件外的用户行为,屏幕上的图像是如何在视觉上更新的。

在本章后续部分,你会看到现在作为参考的内容,即使用submat函数更改Mat对象中部分区域的颜色,这里先把代码片段展示出来。

现在你可以开始享受使用OpenCV的所有功能了。我们来使用吧。

问题定义Mat(矩阵)对象是OpenCV框架的核心,掌握它你可以更加得心应手地使用OpenCV。解决方法让我们通过几个核心示例来看看如何创建矩阵对象并查看它们的内容。工作原理此攻略需要你完成与前几节相同的配置。要创建一个每个“点”只有一个通道的简单矩阵,通常用到Mat类中以下三个静态函数中的一个:zeros,eye,ones。通过表1-2可以更清楚地看到这三个函数的用途。

如果你之前使用过OpenCV(如果还没有,请相信我),你会记得CV_8UC1是OpenCV对8位无符号字的称呼,每个像素一个通道,所以最终有3×3即9个值。正如你所料,它的“堂兄”CV_8UC3给每个像素分配了三个通道,因此1×1的Mat对象就具有三个值。在处理RGB图像时你将经常使用三通道的Mat。它也是加载图像时的默认格式。第一个例子简单地显示了加载每个像素为单通道的Mat对象的三种方法,以及加载每个像素包含三个通道的Mat对象的一种方法。

输出矩阵:

在许多情况下,你可能并不会从头创建矩阵,而是从文件中加载图像。

问题定义加载图像文件,并把它转换为Mat对象以进行数字操作。解决方法OpenCV有一个名为imread的简单函数,用以从文件中读取图像。它通常只需要图像在本地文件系统上的文件路径,但同时这个函数还带有一个缺省的类型参数。让我们看看如何使用不同形式的imread。工作原理imread函数位于Imgcodecs类的同名包中。它的标准用法是简单地给出文件的路径。假设你已从Google搜索下载了猫咪图像并将它存储在images/kittenjpg路径下(如图1-8所示),如下代码给出了如何加载这个图像:

需要注意的是,如果找不到该文件,OpenCV也不会抛出任何异常或者报告任何错误信息,而是显示加载的Mat对象为空,所以没有行和列:

你可以根据自己的编码方式,尝试封装检查Mat大小的代码,以确保可以找到图像并正确解码。这个函数也可以加载灰度图像(如图1-9所示),这是通过传递另外一个参数控制的。

这个参数取自同一个Imgcodecs类。在这里,我们使用IMREAD_GRAYSCALE将图像强制转换为灰度图像并加载到Mat对象中。除了使用IMREAD_GRAYSCALE外,还可以向imread函数传递其他选项来得到特定的处理通道和图像深度,其中最有用的如表1-3所示。

图1-10显示了使用REDUCED_COLOR_8加载得到的图像。

问题定义使用OpenCV保存图像。解决方法OpenCV有一个同imread函数相对应的用来写入文件的函数,函数名是imwrite,也在Imgcodecs类中定义。通常情况下,该函数仅使用本地文件系统里指向图像存储位置的文件路径作为参数,但它也可以使用一些参数来修改图像存储的方式。工作原理imwrite函数同imread函数工作原理相似,不同之处是它除了路径,还需要一个Mat对象来存储图像。第一个代码片段简单地实现将以彩色形式加载的猫咪图像存储到文件中。

图1-11展示了输出的.jpg图片的内容。

现在,当保存Mat对象时,你也可以仅通过使用一个不同的扩展名来改变存储格式。例如,想要保存为便携式网络图形(PortableNetworkGraphic,PNG)格式,仅需调用imwrite函数时,使用一个不同的扩展名即可。

问题定义只保存图像指定的子区域。解决方法这篇简短的攻略的主要目标是介绍submat函数。submat的返回值是一个矩阵对象,内容是原图的子矩阵或子区域。工作原理读入一张猫咪图片,通过submat来截取我们想要的那部分内容。这个例子使用的猫咪图片如图1-12所示。

当然,可以使用任何一张你喜欢的猫咪图片。现在,让我们使用imread来读取这个文件。

根据观察可知,println输出了矩阵对象本身的一些信息。它的大部分信息与内存有关,所以你可以直接访问内存,同时它也显示了这个矩阵对象是否是一个子矩阵。在这个例子中,由于这个矩阵对象是原始图片,所以它的isSubmat值是false。

现在我们使用submat函数的第一种形式,输入参数是每一行和每一列的起始和终止值。

输出的对象显示新创建的矩阵对象确实是一个子矩阵。

有一件很好的事情是,当你对子矩阵进行了操作之后,原始矩阵也会受到同样的影响。例如,你对子矩阵中猫咪的脸进行了模糊处理,并且保存了整个矩阵(不是子矩阵),那么就只有猫咪的脸会变得模糊。具体操作如下所示:

blur是org.opencv.imgproc.Imgproc类中的一个核心函数,它的输入参数是size对象,用来指明每个像素模糊区域大小,size越大,模糊的效果也越强。模糊的结果如图1-15所示,当你仔细看的时候会发现,只有猫咪的脸部被模糊了,这也是我们之前保存的子矩阵的位置。

你之前也见到过submat函数的其他定义,还有两种方法可以获得子矩阵。一种是采用两个Range参数,第一个代表行(y或高度)的范围,第二个代表列(x或宽度)的范围,都是使用Range类来创建的。

后一种方法最常用,因为它最自然。同时,当在图片中检测物体时,你可以用该物体的包围框,它的类型是Rect对象。值得注意的是,修改子矩阵会破坏原矩阵的效果。如果你想把子矩阵改成蓝色:

submat3_2.png和submat3_3.png都会变成如图1-16所示的蓝色猫咪脸。

同时原矩阵也会被变成如图1-17所示的样子!

这里想表达的观点是,无论在何时何地使用submat函数,一定要小心谨慎,通常情况下,它是一个强有力的图像处理工具。

问题定义让我们来学习如何手动地通过多个子矩阵生成一个完整的矩阵。解决方法setTo和copyTo是OpenCV中两个非常重要的函数。setTo可以将一个矩阵中的所有像素设置为指定的颜色,而copyTo可以将一个已有的矩阵复制到另一个矩阵之中。当使用setTo或者copyTo时,你经常需要与子矩阵打交道,即只对矩阵中的一部分进行处理。为了使用setTo,我们会用到OpenCV的Scalar对象来定义颜色,这里会使用RGB颜色空间的一组值来创建。让我们来看一下具体是怎么工作的。工作原理第一个例子使用setTo将多个子矩阵合成一个矩阵,每个子矩阵有不同的颜色。从彩色子矩阵生成矩阵首先我们通过RGB值来定义颜色。之前提到过,颜色是通过Scalar对象创建出来的,包含三个整数值,每个值的范围是0~255。第一个颜色值代表蓝色的深度,第二个值代表绿色的深度,最后一个值代表红色的深度。为了得到红色、绿色或者蓝色,可以把对应的颜色值设为最高值,即255,其他值设为0。下面的例子介绍了如何得到红色、绿色和蓝色。

品红是绿色的补充色,黄色是蓝色的补充色,它们的值如下所示:

我们把颜色都设置好了,现在使用这些对象来创建一个包含所有颜色的矩阵。接下来的setColors方法把输入的矩阵中的一行填充为主颜色RGB或补充色CMY。我们来看一下如何使用setTo将子矩阵设置为给定的Scalar颜色。

接下来,我们创建一个包含三个颜色通道的矩阵,并且填充它的第一行和第二行。

结果是一个包含两行的矩阵,如图1-18所示,每一行都包含不同颜色的子矩阵。

从图片子矩阵生成矩阵颜色很棒,但是你也许更希望能处理图像。第二个例子介绍如何使用图像填充子矩阵。首先创建一个大小为200×200的矩阵和两个子矩阵:一个是主矩阵的上部,一个是主矩阵的下部。

然后加载一个图片以创建另一个小矩阵,并把它的大小调整为上部(或下部)的子矩阵大小。这里会引入Imgproc类中的resize函数。

当然,你可以任意选择其他的图像。这里,假设加载的图像如图1-19所示。

这个猫咪矩阵被复制到上部子矩阵和下部子矩阵。请注意,之前设置大小的步骤很关键。复制能够成功,是因为小矩阵和子矩阵的大小是完全相同的,因此复制的时候没有出现任何问题。

生成的matofpictures.jpg文件包含两只猫咪,如图1-20所示。

如果你忘了调整小矩阵的大小,那么复制会彻底失败,结果可能会是如图1-21所示的样子。

问题定义一张图片中包含一组物体、动物或者形状,也许是因为你想得到图像中物体的个数,想把它们高亮显示出来。解决方法OpenCV提供了一个非常有名的函数叫作Canny,它可以高亮显示图像中的线条。本章的后几节会详细介绍Canny的用法。我们先使用Java来实现一些简单的操作。OpenCV的Canny函数可以检测灰度矩阵中的轮廓。我们需要做的只是把输入的矩阵转换为灰度图像,剩下的工作将由Canny完成。通过Core类中的cvtColor函数,OpenCV可以很容易地改变颜色空间。工作原理假设你有一张工具图片,如图1-22所示。

和往常一样,我们把图片加载到矩阵中。

接下来,使用cvtColor函数来进行颜色转换,它的输入包含源矩阵、目标矩阵和目标颜色空间。颜色空间的常量可以在Imgproc类中找到,它们的名字以COLOR_为前缀。使用颜色常量COLOR_RGB2GRAY,可以把矩阵变成黑白两色。

这个黑白图像可以被直接送入Canny中。Canny函数包含以下参数:■源矩阵■目标矩阵■低阈值,使用150.0■高阈值,通常是低阈值的2倍或3倍■光圈,3~7之间的一个奇数,我们使用3。光圈值越大,被检测到的轮廓越多■L2梯度,暂时设置为true对每一个像素,Canny使用一个卷积矩阵包含一个核心像素和它的邻居像素,得到一个梯度值。如果梯度值大于高阈值,那么它就被检测为边界。如果梯度值在高阈值和低阈值之间,并且有个高阈值和它连接,那么它也会被保留。接下来,我们来调用Canny函数。

输出的图片如图1-23所示。

为了保护眼睛、节省打印机油墨和树木资源,有些时候把矩阵中的白色变成黑色、黑色变成白色会让物体更容易辨认。反色操作可以通过Core类中的bitwise_not函数实现。

当然,也可以把Canny函数用在更多的猫咪图片中。图1-25~1-27展示了同样的Canny函数用在猫咪图片中的效果。

问题定义Canny的边缘检测非常棒,它的输出还可以被作为掩膜(mask),用于生成一个精美的艺术化图片。让我们来尝试把Canny的结果画在另一张图片上。解决方法当进行复制操作时,可以使用一个叫作掩膜的参数。掩膜是一个单通道的矩阵,值只包含0和1。当使用掩膜进行复制时,如果掩膜中的像素值是0的话,源矩阵中的像素就不会被复制,如果值是1的话,源像素就会被复制到目标矩阵中。工作原理在1.10节攻略中,根据bitwise_not函数输出的结果,我们得到了一个新的矩阵对象。

如果你决定把kittens输出的话(也许不是一个好主意,因为文件很大),你会看到一堆0和1,这就是掩膜的制作方法。现在有了掩膜,我们来创建一个叫作target的白色矩阵,作为copy函数的目标参数。

然后为copy函数加载一个源矩阵,你应该记得,我们需要确定它的大小和copy函数的目标矩阵(也就是target矩阵)的大小一致。让我们来调整背景对象的大小。

这样我们就准备好进行复制操作了。

输出的矩阵如图1-28所示。

问题定义在Canny操作的结果中,希望找到一组可绘制的轮廓,并把它们绘制在矩阵中。解决方法OpenCV中有两个函数常与Canny函数一同使用:findContours和drawContours。findContours读入一个矩阵,并在这个矩阵中查找边缘,或者说定义形状的边界。因为原图像可能包含许多颜色和亮度的噪声,你通常需要一个经过预处理的图片,即一个由Canny处理过的黑白矩阵。drawContours读入findContours的结果,也就是一组轮廓对象,并允许你用具体的特征来绘制这些轮廓,例如绘制线条的粗细和颜色。工作原理如同在解决方法中提到的,OpenCV的findContours函数输入一个预处理过的图片,包含以下参数:1.预处理过的矩阵2.用于接收轮廓对象的空队列(MatOfPoint)3.一个分层矩阵,你目前可以忽略它,并把它设置为空矩阵4.轮廓追踪模式,例如是否建立轮廓之间的关系或返回所有内容5.存储轮廓的近似类型,例如是绘制所有的点还是只绘制一些关键点第一步,我们把预处理图片和追踪轮廓一起放在自定义的find_contours函数中。

该函数返回一组检测到的轮廓,每个轮廓包含一组像素点,用OpenCV的话说,就是一个MatOfPoint对象。接下来,我们定义一个draw_contours函数,读入源矩阵来找出第一步中得到的每个轮廓的大小,输入还包括我们希望用来绘制边缘的线条粗度。在OpenCV中绘制轮廓,通常需要一个for循环,并把要绘制的轮廓索引给drawContours函数。

太棒啦,该攻略最核心的部分已经完成,现在你可以运行它了。可以和之前一样使用猫咪的照片来作为基准输入图像。

draw-contours的结果如图1-2所示。

接下来换一种粗度来绘制轮廓,例如,当粗度是3时,结果会有些许不同,如图1-30所示,线条更细一些。

从现在开始,我们可以使用结果矩阵作为掩膜进行背景复制。下面的代码取自1-11节。该函数读入一个掩膜,并且用这个掩膜进行复制。

图1-31显示了掩膜复制的结果,其中轮廓绘制时的粗度为3。

值得注意的是,第3章将介绍更酷的使用掩膜和背景的方法,用于生成艺术图片,这一节攻略暂时告一段落。

实时处理好吧,坏消息是OpenCV的Java封装不包含将矩阵转为BufferedImage的明确方法,BufferedImage是Java的graphic包中处理图像的对象。这里不介绍太多细节,假设你需要一个MatToBufferedImage函数来实时处理Java帧,通过把矩阵对象转换为BufferedImage,即可将它渲染为标准的JavaGUI对象。让我们快速地写一个函数,将矩阵转换为标准的JavaBufferedImage。

好了,标准OpenCV包中缺少的代码已经被实现了,你可以直接创建JFrame来接收矩阵对象。

本攻略的最后一步是使用一段与do_still_captures函数类似的代码,但并不在几帧之后停下,你将会写一个无限循环来处理视频流。

图1-33展示了一个日本房间在凌晨1点钟的实时景象,通过JFrame实时渲染。显然,目标是实时处理矩阵对象,对于你来说一个很好的练习是试着生成图1-34所示的屏幕截图效果。

答案如下所示,你也应该猜到了,这段代码只是将Canny函数应用在视频读取的矩阵对象中。

问题定义既然你已经可以使用Java写一些OpenCV代码了,并且开始享受它,但此刻你想要使用Scala来减少样板代码。解决方法到目前为止,你使用的当前OpenCV设置可以很容易运行任何为JavaVM编译的类。因此,如果你能够编译Scala类,并且正好有Leiningen插件,那么剩下的工作就十分相似了。那意味着通过到目前为止已经使用的Leiningen设置,你仅需要更新project.clj文件中的项目元数据,该文件存放于几个地方来确保运行正常。该工作需要两步。第一步,添加Scala编译器和库;第二步,更新目录,使Scala代码文件可以被找到。工作原理基本设置project.clj文件需要在如下重点陈述的几个地方被更新。**■项目名称,当然那是可选的。■主类,你可以使用同样的名称,但如果那样做,确保使用leinclean命令删除旧的Java代码。■接下来添加lein-zinc插件,这是一个集多能于一体的Leiningen插件。■lein-zinc插件需要在lein执行编译前触发,因此我们需要在项目元数据中的prep-tasks键中添加一步。prep-tasks键负责定义在相似命令执行前需执行的任务。■最后,将Scala库依赖加入到依赖键中。**更新的project.clj文件如下。

你为Scala建立的新项目文件结构应该看上去如图1-35所示。

当编译上述代码时,Scala源代码会在目标文件夹中生成一些Java字节码,就像Java代码生成的方式一样。因此,你可以像在Java中做的一样来运行Scala代码,或者通过命令行运行:

在屏幕上,控制台输出预期的OpenCV的3x3矩阵。

图1-36展示了Scala更新设置元素的全景图。

模糊第一个Scala示例的确显得有点太简单了,那么现在让我们在Scala中试试OpenCV的模糊效果。

就像你看到的,模糊效果在一行中被连续调用多次,可以在同一个矩阵对象上增加模糊效果。图1-37中这只无聊猫咪被模糊成了图1-38中的模糊无聊猫咪。

代码中使用了Canny参数以在这个简单的艺术空间中输出一些结果,但这一次并没有很有效地找出边缘。图1-39和图1-40展示了在加载的猫咪图像上使用Canny效果处理前/处理后的结果。为Java编写的画轮廓示例也被引入到Scala中并且提供了源码,位于本书提供的案例源码库中。现在,这个示例留给读者作为一个简单的练习题。

因为Kotlin类是通过插件显式地编译到JavaVM字节码中,你可以参考那些到目前为止你已经完成编译的类。显而易见,第一个测试是检验你是否可以加载一个矩阵对象并且打印它的0和1值。下面十分简短的Kotlin代码片段实现了上述功能。

这个命令输出同样是必要的,展示了正确创建的OpenCV对象并且将它打印到控制台中。

就像你掌握的那样,Kotlin的构造函数调用不需要使用明确的new关键字,而且就像在Scala中一样,可以使用静态引入方法。现在你可以从图1-41中的原始输入图像开始看这段代码的运行效果。你会看到程序创建了三个文件,如图1-42、图1-43和图1-44所展示的三个输出文件所示。

由于本攻略的目的是培养你创造性的想法,因此我们不再深入学习如何编写Kotlin程序以及使用tornadofx库编写Kotlin程序。但是你将很快学习到一些如何将这些方法集成到OpenCV中的Kotlin示例。下面的第一个示例将向你介绍如何引导你的Kotlin代码显示一帧中的一副图像。仿制用户接口一个简单的tornadofx应用基本遵循了一个结构,即给定的启动器(Launcher)→应用→视图,如图1-45中的流程图所示。

有了这张图的概念,我们需要创建三个类。**■HelloWorld0:UI应用的主视图■MyApp0:用来发送给JavaFX启动器的JavaFX应用对象■World0:主类,只会被创建一次,因此使用对象代替类来定义它,以此启动基于JVM的应用**一个tornadofx中的视图由一个根面板(RootPanel)组成,你可以按照自己的意愿定制JavaFX小部件作为根面板。**■下面的代码创建一个单一视图,该视图由嵌入在imageview小部件中的图像组成。■imageview中图像的尺寸由定义小部件的模块设置。■视图初始化由init{......}模块完成,而且由于根对象无法再一次初始化,因此使用神奇的with函数完成。**

这段代码的其余部分是标准的tornadofx/javafx样板模板,以此正确启动基于JavaFX的应用。

如同到目前为止你所完成的那样,通过如下命令使用Leiningen自动模式运行上述代码。

你的屏幕上将会出现图形化的一帧(图1-46)。

实际上,这段代码和这一帧有一些不同。在根模块中,通过在合适的地方插入下面的代码片段设置了一个标题。你会找到这是在哪里插入的。

反馈按钮的用户接口接下来的示例基于前述示例并且增加了一个按钮,当按下按钮,内部计数器会增加,而且计数器的值会实时显示在屏幕上。反馈值可以通过SimpleIntegerProperty建立,或者通过javafx.beans包中的Simple-XXXProperty建立。该反馈值可以绑定到小部件上,在接下来的示例中,将会绑定到一个标签上,因此标签值与属性值相同。按钮是你可以用来定义一个处理句柄的UI小部件。句柄代码存在于模块内部或者一个不同的Kotlin函数中。根据上述目标和解释,让我们开始介绍下面的代码片段。

运行计数器应用的结果如图1-47所示。在点击这个漂亮的按钮几次之后,你会得到如图1-48所示内容。

模糊应用这些应用很酷,但这看上去像是创建GUI的课程,而且同OpenCV没有太大关系。确实如此。因此,最后一个Kotlin应用基于上述两个示例,介绍如何建立一个模糊应用,其中模糊程度由反馈属性设置。你需要在Java环境下的图像对象和OpenCV环境下的Mat对象来回转换。下面的示例介绍一种快速转换的方法,通过使用OpenCV的imencode函数实现,该函数将Mat对象编码为字节而无须将它们存储到文件中。这个模糊应用使用了SimpleObjectProperty类型的变量,该变量随着它的图像化视图更新而变化。较长的导入列表有些烦人,但你可能不必为自己自定义的应用加入更多的导入。

通常情况下,Leiningen在文件改变后为你自动完成全部Kotlin编译工作,模糊应用效果如图1-49所示。

当你点击增加(increment)按钮后,猫咪图像变得越来越模糊;当你点击减小(decrement)按钮后,它变得越来越清晰。在本书的代码样本中有更多的tornadofx示例,因此无须犹豫,找出它们来练习。你可能会通过OpenCV方法获得更多的UI。例如一个图像拖拽面板,图像可以根据你的意愿被模糊处理。那听起来不再是无法实现的,是吗?

第1章写满了攻略,从在基于JavaVM的OpenCV中建立一个小项目开始,逐渐学习更加复杂的图像操作示例,最开始使用Java,最终熟练使用JavaVM运行环境,以使用Scala代码以及含有令人印象深刻的tornadofx库的Kotlin代码。介绍origami库的大门已经打开,该库是为OpenCV设计的Clojure封装。该环境带给你更加简洁的代码并且更具有交互性,以此来尝试新事物并且变得更加有创造性。是时候兴奋起来了。**我对未来有兴奋的感觉,而我不知道那看上去会是什么样。但是无论如何,未来将会是我创造的样子。——AmandaLindhout**

THE END
1.详解JavaWeb项目中DLL文件动态加载方法动态加载DLL文件的方法 在JavaWeb项目中,我们可以使用System.load()方法或者System.loadLibrary()方法来动态加载DLL文件。下面分别介绍这两种方法。 使用System.load()方法 System.load()方法用于加载指定路径的DLL文件。以下是一个使用System.load()方法的示例: https://blog.51cto.com/u_15702012/12830001
2.高效文件处理:Pythonpathlib实战指南在使用Python处理文件路径时,强烈建议使用pathlib。 pathlib以面向对象的方式处理文件路径,既避免了很多陷阱,也能使执行许多路径的相关操作变得更容易。 本篇总结了常用的使用pathlib进行文件路径处理的方法。 1. 常用操作 首先介绍如何使用pathlib来完成一些常规的文件路径相关操作。 https://blog.csdn.net/fenfenfen520a/article/details/144423375
3.System.loadLibrary()的使用方法汇总.HuajianHsiu当使用System.loadLibrary()调用 Dll,两种方法: 1.设定环境变量。 比如:所编辑的Dll在目录“D:/cppProjects/nativecode/release”内,将这个路径复制添加到电脑的环境变量中的path变量内即可。 2.设定项目属性。(开发推荐) 右击项目名|选择属性properties|在左边列表内选择“Java Build Path”|在右边选项卡用选择“sohttps://www.iteye.com/blog/huajianhsiu-1828242
4.Python中pathlib库的使用pathlib 提供了多个类来表示不同类型的路径,其中最常用的是 Path 类。Path 类实例化后可以用于访问文件和目录的属性、方法,比如文件大小和修改时间,以及创建、重命名、删除等操作。使用 pathlib 库可以使得代码更加易读和可维护,尤其在涉及到多平台的应用时,可以避免手动拼接文件路径的麻烦和错误。1、获取当前路径https://baijiahao.baidu.com/s?id=1763596157090095701&wfr=spider&for=pc
5.Android包体积优化方案动态资源管理系统Android社区而我们这里,由于so文件不存在于apk当中,而是需要动态下载,所以我们显然不能直接使用系统的System.loadLibrary方法加载so文件。 而动态加载so的方法,在热修复和插件化框架中,已经比较成熟了,我们参考了市面上的开源框架后,选择了腾讯的Tinker框架的加载方案,即使用反射classloader 将 so 包的路径写入 nativeLibraryPathElehttp://androidos.net.cn/doc/2023/6/4/802.html
6.loadlibrary使用的方法有哪些问答在使用loadlibrary函数加载动态链接库时,有以下三种方法:1. 静态链接:将动态链接库直接包含在可执行文件中,使得程序在启动时就能访问动态链接库中的函数和数据。静态链接是最简单的加载动态链接库https://www.yisu.com/ask/16417239.html
7.厉害了,Python也能使用动态链接库腾讯云开发者社区动态链接库的调用方法很多,任你挑选。 代码语言:javascript 复制 CDLL(xx.dll)OleDLL(xx.dll)PyDLL(xx.dll)WinDLL(xx.dll)cdll.LoadLibrary(xx.dll)oledll.LoadLibrary(xx.dll)pydll.LoadLibrary(xx.dll)windll.LoadLibrary(xx.dll)#也可使用链接库读取器LibraryLoader,它也同样支持上面的八种方式LibraryLoader(Chttps://cloud.tencent.com/developer/article/2015957
8.好学编程:六种打包Python代码方法优缺点分析这是怎么回事呢?那么问题来了,sqlalchemy这个库在初始化的时候是不需要显示导入引擎库的,它自己有一个create_engine()的函数来初始化,这个字符串是使用者根据规则来自己填写的。其实解决的方法很简单,我们只要在显式导入pymysql这个库即可。现在我们导入这个库:import pymysql https://zhuanlan.zhihu.com/p/662816631
9.详解Java中native方法的使用javanative是与C++联合开发的时候用的!使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。本文给大家介绍java 中native方法使用,感兴趣的朋友一起看看吧 今天在网上学习时碰到有关于 native修饰符所修饰的方法,上网查了查,觉得很有意思记录一下 https://m.jb51.net/article/195760.htm
10.PyTorch自然语言处理入门与实战本书首先对自然语言处理进行了概述,并介绍了Python自然语言处理基础;然后介绍了什么是PyTorch和PyTorch的基本使用方法;接着介绍了多种机器学习技术及其在自然语言处理中的应用,包括RNN、词嵌入、Seq2seq、注意力机制、Transformer 和预训练语言模型;最后介绍了用自然语言处理实现的两个项目,即“中文地址解析”和“诗句https://www.epubit.com/bookDetails?code=UB7da415821f9de&id=UB7da415821f9de
11.软件测试试题库(通用7套)第二种为编程的.时候使用LoadLibrary,FreeLibrary,GetProcAddress来动态装载程序。则编好的程序使用库文件时,是程序自己主动载入。 10、白盒测试有哪些覆盖方法? 答:语句覆盖,判定覆盖,条件覆盖,判定/条件覆盖,组合覆盖,路径覆盖。 11、进程与线程的区别? https://www.unjs.com/zuixinxiaoxi/ziliao/20170720000008_1398848.html
12.基于FPGA和USB2.0协议的通用数据传输设计AET设计中在此基础上封装了读寄存器、写寄存器、读FIFO、写FIFO四个函数,并以DLL形式提供给用户。上层使用的是MATLAB平台,需要在该平台上配置MEX编译器,并使用loadlibrary函数以加载动态链接库,加载后便可以调用DLL里提供的读写函数了。DLL也能被其他平台调用。http://www.chinaaet.com/article/3000024514
13.java如何调用pythonjava教程java 调用 python 代码的方法:使用 java native interface (jni):加载 python 解释器、获取引用、执行代码、获取结果。使用 python for java (jython):导入库、创建解释器、执行代码、获取结果。 如何在 Java 中调用 Python Java 和 Python 是两种流行的编程语言,有时需要在同一个应用程序中使用它们。Java 可以调用https://www.php.cn/faq/830246.html
14.Delphi中LoadLibrary的使用心得API如果进行FreeLibrary,刚好这个类以外别的地方也加载过这个DLL,也要用这个DLL时,岂不就会有问题了?想了各种各样的检测方法和管理方法,依然觉得有逻辑漏洞。那么能不能不手动FreeLibrahttps://www.2ccc.com/news/Html/?481.html
15.LoadLibraryA函数(libloaderapi.h)模块的名称。 这可以是库模块(.dll 文件)或可执行模块(.exe 文件)。 如果指定的模块是可执行模块,则不会加载静态导入;而是使用DONT_RESOLVE_DLL_REFERENCES标志LoadLibraryEx加载模块。 指定的名称是模块的文件名,与库模块本身中存储的名称无关,由模块定义 (.def) 文件中的LIBRARY关键字指定。 https://docs.microsoft.com/zh-cn/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya
16.如果一个exe使用LoadLibrary函数加载了自己吾爱破解众所周知,exe文件可以使用LoadLibrary函数可以加载一个dll并和GetProcAddress函数连用即可获取到dll中导出函数https://www.52pojie.cn/thread-1935537-1-1.html
17.如果我使用loadlibraryexw()加载相同的.dlltwitce,则会发生什么handle = ::LoadLibraryExW(dllpath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) 当ASLR(DynamicBase)打开时,它会返回相同的地址吗? 看答案 没有什么不同而不是没有aslr。第二个LoadLibraryex调用将返回与第一个呼叫相同的手柄,并且DLL的使用计数递增。 所以DLL没有加载“两次”。是在第一个呼叫上加载。 除了不同https://www.pianshen.com/question/78471527993/
18.ctypes通过cdll.msvcrt 调用的标准 C 函数,可能会导致调用一个过时的,与当前 Python 所不兼容的函数。因此,请尽量使用标准的 Python 函数,而不要使用 msvcrt 模块。在Linux 中,要求指定文件名 包括 扩展名来加载库,因此不能使用属性访问的方式来加载库。 你应当使用 dll 加载器的 LoadLibrary() 方法,或是应当通过调用http://docs.python.org/zh-cn/3/library/ctypes.html