当前位置: 首页 > news >正文

当OpenCV遇上Origami

导语

作为计算机视觉库,OpenCV一直被广泛应用于各种图像和视频相关的项目中,更在推动人工智能和神经网络的发展中发挥了重要作用。但是学习和使用OpenCV并不简单,总是需要些时间选择合适的类库、合适的编译工具、合适的编译配置等。 
由Clojure封装Origami的愿景是用令人愉悦的语法,直截了当地发挥OpenCV的巨大威力。小编希望本文能帮助你集中更多的时间和精力在项目本身而非工具上。 

      作者:Nicolas Modrzyk

      来源:华章计算机(ID:hzbook_ jsj)

Origami库诞生的动机是使得计算机视觉相关的编程更易于开展、运行,并且便于实验。

最近,人工智能和神经网络风靡一时,我需要完成一项任务,为几个不同的神经网络准备并且生成数据。显而易见的是,我不可能仅仅通过将所有种类的图像或者视频数据输入到一个网络里就期待它们产生效果。我需要将这些图像或视频按照尺寸、颜色或者内容组织起来,并且尽可能地自动化处理这些图像,因为手动将这十亿张图像分类确实很耗时。

因此,在本文中,我们将介绍Origami,它是为基于JavaVM的OpenCV库所设计的Clojure封装、项目模板以及工作环境样本,所有这些都使用了简洁明了的编程语言。

本文的案例将会使用Origami完成,即你将通过Clojure学习OpenCV代码。我们将从Origami库的角度学习OpenCV的一些概念。

 

开始Origami编程

生活本身是简单的……但这并不意味着生活是容易的。

Steve Marabol

 

问题定义

现在,你已经听说了名为Origami的库,它将OpenCV封装成轻量级的特定领域语言(Domain Specific Language,DSL),你想要在你的机器上安装Origami并且尝试使用它。


解决方法

如果你已经阅读或翻阅了本书的第一章,你会记得使用了Leiningen来创建一个工项目模板并且在一个简单的项目层面布局文件。

现在,你将使用一个名为clj-opencv的项目模板,这个模板将会为你下载依赖关系并且复制需要的文件。

你将看到在这个新的设置中可以使用不同的编程风格。


工作原理

如果Leiningen一直安装在你的机器上,同建立基于OpenCV的java项目一样,你可以基于模板建立一个新的项目。

使用Leiningen模板进行项目设置

项目模板叫做clj-opencv,在终端或控制台通过Leiningen使用下面的一行命令调用:

lein new clj-opencv myfirstcljcv

执行该命令将下载新的模板并且建立包含如下内容的myfirstcljcv文件夹:

├── notes

│ ├── empty.clj

│ └── practice.clj

├── output

├── project.clj

├── resources

│ ├── XML

│ │ ├── aGest.xml

│ │ ├── closed_frontal_palm.xml

│ │ ├── face.xml

│ │ ├── fist.xml

│ │ ├── haarcascade_eye_tree_eyeglasses.xml

│ │ ├── haarcascade_frontalface_alt2.xml

│ │ └── palm.xml

│ ├── cat.jpg

│ ├── minicat.jpg

│ ├── nekobench.jpg

│ ├── souslesoleil.jpg

│ └── sunflower.jpg

└── test

└── opencv3

├── ok.clj

├── simple.clj

├── tutorial.clj

└── videosample.clj

 

你会看到6个文件夹,19个文件。在上述文件结构中:

  • notes文件夹包含了为gorilla和lein-gorilla建立的注释形式的代码。我们稍后将会讲解如何使用这两只“野兽”。

  • project.clj在Leiningen项目文件里已经出现过。

  • resources包含了样本图像、练习用的XML文件以及OpenCV识别特征。

  • test包含了样本Clojure代码,该代码实现了如何开始使用OpenCV和Origami。

如同你所记得的样子,project.clj文件包含了几乎所有项目元数据。这一次我们将使用一个同第一章相比稍作更新的版本。

同前一章不同的主要地方在下面加粗显示,这样可以使我们快速审阅代码。

(defproject sample5 "0.1-SNAPSHOT"

:injections [

(clojure.lang.RT/loadLibrary org.opencv.core.Core/NATIVE_

LIBRARY_NAME)]

:plugins [[lein-gorilla "0.4.0"]]

:test-paths ["test"]

:resource-paths ["rsc"]

:main opencv3.ok

:repositories [

["vendredi" "https://repository.hellonico.info/repository/

hellonico/"]]

:aliases {"notebook" ["gorilla" ":ip" "0.0.0.0" ":port" "10000"]}

:profiles {:dev {

:resource-paths ["resources"]

:dependencies [

; used for proto repl

[org.clojure/tools.nrepl "0.2.11"]

; proto repl

[proto-repl "0.3.1"]

; use to start a gorilla repl

[gorilla-repl "0.4.0"]

[seesaw "1.4.5"]]}}

:dependencies [

[org.clojure/clojure "1.8.0"]

[org.clojure/tools.cli "0.3.5"]

[origami "0.1.2"]])

 

如同预期,origami库被添加到了依赖关系(dependencies)部分。

一个名为gorilla的插件也被添加进来。这个插件将帮助你运行python笔记本风格的代码;我们将会在本攻略的后续部分介绍它。

注入(injections)部分一开始看上去有些不清楚,但同第一章中的问题一样,它会在启动运行环境时已经加载本地OpenCV库,因此你不需要在所有的示例中重复它。

万事俱备

需要运行的主命名空间是opencv3.ok;让我们现在就运行它来确保上述设置是正确的。运行的方式同第一章相比没有变化,在终端或控制台使用同样的命令来加载代码:

lein run

在几行简短的输出后,你应该看到如下输出:

Using OpenCV Version: 3.3.1-dev ..
#object[org.opencv.core.Mat 0x69ce2f62 Mat [ 1200*1600*CV_8UC1,
isCont=true, isSubmat=false, nativeObj=0x7fcb16cefa70,
dataAddr=0x10f203000 ]]
A new gray neko has arisen!

 

运行后会在项目文件夹中生成grey-neko.jpg文件,该图像如图2-1所示。

 

图2-1 Grey Neko

opencv3.ok命名空间代码全部如下所示:

(ns opencv3.ok
    (:require [opencv3.core :refer :all]))
(defn -main [& args]
  (println "Using OpenCV Version: " VERSION "..")
  (->
   (imread "resources/cat.jpg")
   (cvt-color! COLOR_RGB2GRAY)
   (imwrite "grey-neko.jpg")
   (println "A new gray neko has arisen!")))

你会认出在前面章节中使用的OpenCV函数imreadcvtColorimwrite,由此证明java OpenCV函数确实封装在了Clojure中。

第一个使用origami DSL编写的代码序列流如图2-2所示。 

 

图2-2 第一个Origami示例代码流

网络摄像头检查

如果你有网络摄像头插件,本书提供一个开启摄像头和视频流的示例。运行示例的文件是samplevideo.clj。

和以前一样,你可以在lein run命令中明确命名空间来开始执行这个示例。

lein run -m opencv3.videosample

当命令开始执行后,你将会看到敲代码所在的咖啡店的运动图像,如图2-3所示。

图2-3 东京咖啡店

虽然这只是运行项目模板的示例,但你已经可以开始在你自己的文件中编写你的实验代码,并且通过lein run命令运行它们。

自动插件应用

你很快就会发现为什么这样并不是使用Origami的最好方式,因为每次都会重新编译你的源文件。当然,重新编译源文件是用来检查所有代码编译并且保证正确运行的技术。

所以下面是对于如何在Clojure/Origami代码中建立插件解决方案的快速提醒,如同在第一章中为Java、Scala和Kotlin建立的插件解决方案一样。

修改project.clj文件,添加lein-auto插件以使它符合下面的代码:

  :plugins [[lein-gorilla "0.4.0"][lein-auto "0.1.3"]]
  :auto {:default {:file-pattern #"\.(clj)$"}}

这段代码默认不在项目模板里,因为很大程度上在其他大部分时间里,你并不会需要它。

一旦你添加了这段代码,你可以通过之间你所见到的自动运行的命令来运行它:

lein auto run

主命名空间将被执行,并且等待文件改变以重新编译和执行。

因此,在按照如下方式修改了ok.clj文件主方法后:

(defn -main [& args]
    (->
     (imread "resources/cat.jpg")
     (cvt-color! COLORMAP_JET)
     (imwrite "jet-neko.jpg")
     (println "A new jet neko has arisen!")))

你会看到一个新生成的jet-neko.jpg文件,展示了一只新的可爱的猫咪,如图2-4所示。

 

图2-4 名为Jet的猫咪

既然自动运行插件设置完全正确,让我们考虑如何通过使用Clojure REPL来缩短你的代码输入和处理结果输出之间的延时。

使用REPL

我们刚刚介绍了如何运行项目并编写了一些时尚的Origami代码,这如同在Java、Scala和Kotlin中的设置一样;并且再一次学习了如何引入和使用自动运行插件。

更强大的是,Clojure带来了Read-Eval-Print-Loop(REPL)环境,意味着你可以像输入命令一样一行一行地输入几行代码,并且让它们立即执行。

为了使用Clojure REPL,Leiningen提供了名为repl的子命令,执行如下:

lein repl

在终端/控制台打印了几行启动信息后:

nREPL server started on port 64044 on host 127.0.0.1 -
nrepl://127.0.0.1:64044
REPL-y 0.3.7, nREPL 0.2.11
Clojure 1.8.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_151-b12
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
   Source: (source function-name-here)
  Javadoc: (javadoc java-object-or-class-here)
     Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e

你会看到欢迎提示:

opencv3.ok=>

opencv3.ok是项目的主命名空间,你可以像在opencv3/ok.clj文件中输入代码一样在提示行中输入代码。例如,让我们通过输出底层OpenCV库的版本号来检查它是否加载正确:

(println "Using OpenCV Version: " opencv3.core/VERSION "..")
; Using OpenCV Version: 3.3.1-dev ..

结果证明该库的确被正确加载了,并且通过Leiningen的机制进行了本地绑定。

现在让我们使用它快速开始后面的学习吧。下面的两行实现从utils命名空间获取一些函数,用来打开一个frame控件,然后加载一张图像并且在控件中显示它。

(require '[opencv3.utils :as u])
(u/show (imread "resources/minicat.jpg"))

现在,图2-5中这只可爱的猫咪应该出现在你的电脑上了。

 

图2-5 可爱的猫咪

Origami推崇使用流水线(pipeline)的概念进行图像处理。因此为了读取一张图片,转换加载图像的颜色空间,并且在控件中显示处理后的图像,你通常会通过Clojure线程宏(threading macro)->将函数调用一一罗列,就像下面一行代码:

(-> "resources/minicat.jpg" imread (cvt-color! COLOR_RGB2GRAY)(u/show))

该代码实现了将图2-5中的minicat.jpg转换为图2-6中的灰色版本。

 

图2-6 灰色可爱的猫咪

->实现了重新组织代码以使第一个调用的输出结果传递到下一行,以此类推。这是为迅速和紧凑的图像处理代码设计的。

可以注意到的是,各行是直接执行的,因此你不需要等待文件改变或其他事件,而且在按下Enter键后即可在屏幕上得到结果。

及时行乐吧。

 

及时行乐需要太长的时间。

Carrie Fisher

使用Atom编写REPL

从Leiningen开始使用REPL很方便,你可以在说明文档中发现很多其他的特征,但是这还是很难同标准的文本编辑器提供的自动补全功能相竞争。

Atom编辑器采用了与project.clj文件同样的项目元数据,实际上可以通过插件提供快速可见完成结果的选择。

需要安装的插件名为proto-repl。很高效,你仅需要安装两个插件。

  • ink插件,prot-repl需要该插件

  • proto-repl插件

通过上述安装,你可以在你的Atom编辑器中获取如图2-7所示相同的设置。

图2-7 在Atom中安装两个插件:ink和proto-repl

可以通过图2-8所示的Atom菜单或者等效的快捷键来启动同样基于Leiningen的REPL。

 

图2-8 在Atom中启动REPL

启动REPL后,在Atom编辑器右侧开启了一个名为Proto-REPL的窗口。这个REPL就是你在终端直接执行lein repl命令的那个REPL。因此,你也可以在这里输入代码。

其实,这次设置更炫酷的地方在于为你提供了输入代码时的自动补全以及方法列表功能,如图2-9所示。

 

图2-9 插件自动补全功能

现在,你可以重新输入那段用来直接在文件中读取图片、转换颜色空间的代码,就叫它ok.clj吧。你的设置应该同图2-10所示的相似。

 

图2-10 Atom编辑器和Clojure代码

一旦你输入了代码,你可以选中一段代码并且通过按下Ctrl-Alt+s键(在Mac电脑中为Command-Ctrl+s键)来执行选中的那些行代码。

你也可以通过按下Ctrl-Alt+b键(在Mac电脑中为Command-Ctrl+b键)来执行光标前的代码块,并且快速获得运行结果。

在代码评估和安排后,你可以在左手边区域快速编写代码,在右手边区域得到图2-11所示的图像变换的反馈。

 

图2-11 理想的基于编辑器的计算机视觉编程环境

这只名为喷气机的猫咪由output.jpg文件展示,并且可以通过在打开的编辑器中更新和执行代码来更新这幅图片。

例如,你可以自己尝试一下,在处理流中添加如下所示的resize!函数调用后会发生什么。

(->
    (imread "resources/cat.jpg")
    (resize! (new-size 150 100))
    (cvt-color! COLORMAP_JET)
    (imwrite "output.jpg"))

很好,一副重新改变尺寸的名为喷气机的猫咪将出现在你的屏幕上。

Gorilla笔记本

为了完成本攻略,让我们首先介绍如何在Origami项目中使用Gorilla。

Gorilla一个是Leiningen插件,在这里你可以编写执行笔记本。

这意味着你可以在写文档的同时写代码,更棒的是,你可以同外面的世界共享这些笔记。

这是如何做到的?Gorilla获取到了你的项目设置,并且使用该设置在REPL背景下执行代码。于是,它可以从project.clj文件中发现origami/opencv设置。

这也会启动一个网页服务器,该服务器用来为笔记或者工作表提供服务。工作表是你用来编写几行代码并且执行它们的页面。

你也可以通过超文本标记语言(HyperText Markup Language,HTML)的格式在表中编写文档。

结果是,每一条笔记或者工作表最终都成为高效的微博客。

project.clj文件和clj-opencv模板一同定义了好用的leiningen别名,以此通过笔记本别名启动Gorilla:

:aliases {"notebook" ["gorilla" ":ip" "0.0.0.0" ":port""10000"]}

它将高效地让Leiningen将notebook子命令转换为下面的gorilla命令:

lein gorilla :ip 0.0.0.0 :port 10000

让我们尝试一下,在控制台或终端中使用如下命令:

lein notebook

几秒钟后,Gorilla REPL启动了。你可以在下面的位置直接访问到:

http://localhost:10000/worksheet.html?filename=notes/practice.clj

你将看到如图2-12所示的工作表。

 

图2-12 Gorilla笔记本和一只猫咪

在Gorilla笔记本中,页面的每一块不是Clojure代码就是标记语言(markdown)文本。你可以通过Alt+g、Alt+m键(在Mac电脑中,Ctrl+g、Ctrl+m键)将当前高亮块转换为文本模式,其中m键代表markdown,如图2-13所示。

图2-13 Markdown测试模式

你也可以通过Alt+g、Alt+j键(在Mac电脑中,Ctrl+g、Ctrl+j键)将高亮的文本块转换回代码模式,其中j键代表Clojure,如图2-14所示。

图2-14 代码块

为了执行高亮的代码块,你可以使用Shift+Enter键,该块转换为可执行模式,如图2-15所示。

 

图2-15 执行了Clojure代码

上面代码所完成的是从代码块中读取,通过websocket将输入发送到背景REPL中,取回结果,并且在代码块底层div中打印它。

为了使导航到一张工作表变得容易,最常用的快捷键在表2-1中展示。

表2-1. Gorilla REPL中最常用的快捷键

Windows/Linux上的快捷键

Mac上的快捷键

作用

到块顶部

到块底部

Shift+Enter

Shift+Enter

检查高亮块

Alt+g, Alt+b

Ctrl+g, Ctrl+b

在当前块前面插入一个块

Alt+g, Alt+n

Ctrl+g, Ctrl+n

在当前块后面插入一个块

Alt+g, Alt+u

Ctrl+g, Ctrl+u

将当前块上移一位

Alt+g, Alt+d

Ctrl+g, Ctrl+d

将当前块下移一位

Alt+g, Alt+x

Ctrl+g, Ctrl+x

删除当前块

Alt+space

Ctrl+space

自动完成选项

Alt+g, Alt+s

Ctrl+g, Ctrl+s

保存当前工作表

Alt+g, Alt+l

Ctrl+g, Ctrl+l

加载一个工作表(或文件)

Alt+g, Alt+e

Ctrl+g, Ctrl+e

将当前工作表另存为一个新的文件名

 

好吧,你已经知道了在Gorilla REPL输入代码所需的所有操作。现在让我们尝试一下吧。在工作表里一个新的代码块中,尝试输入下面的Clojure代码。

(-> "http://eskipaper.com/images/jump-cat-1.jpg"
 (u/mat-from-url)
 (u/resize-by 0.3)
 (u/mat-view))

现在,按Shift+Enter!这将会给你带来如图2-16所示即使反馈效果。

 

图2-16 即时显示的跳跃的猫咪

记住这一些都在浏览器中发生,这有三个直接的正向后果。

第一个是远端的人可以实际看到你的工作表,并且他们可使用他们自己的机器直接连接URL来为你提供文档。

第二个是他们也可以一块一块地执行代码来理解程序流。

第三个是已这样的方式存储工作表格式,可使他们被用作标准的命名空间并且可以通过标准化的代码编写流程来使用。反过来,这也意味着标准Clojure文件可以被打开,并且可通过Gorilla REPL添加文档。

 

从现在开始,我们不再强化使用Gorilla REPL或Atom环境,甚至是仅仅在REPL中输入。实际上,这是同样的项目设置中三个不同的视角。

现在请记住,为了展示一张图片,取决于你正在使用Gorilla REPL还是标准REPL,你将用到的函数是不同的。

在Gorilla REPL中:

(u/mat-view)

在标准REPL:

(u/show)

在Atom中,你将如此保存文件:

(imwrite mat “output.jpg”)

好的,现在你已经准备好了!是时候来尝试解决一些计算机视觉基础问题了。

 

作者简介:Nicolas Modrzyk目前是Karabiner Software的首席技术官和开发团队的领导者。 他还是开源软件社区的活跃贡献者。他也是敏捷方法的热情倡导者,专注于及时完成工作以满足客户需求。

延伸阅读《Java图像处理:基于OpenCV与JVM》

点击上方链接了解及购买

转载请联系微信:zj06220_0

推荐语:本书提供了常见图像处理问题的Java解决方案、学习实践案例,以及有关使用OpenCV进行图像处理的各种知识。

点击这里阅读原文,直达新基建专场

相关文章:

  • 当防范恶意软件时,为什么不像数据科学家那样思考呢?
  • 春分至,燕归来,一心移疫【大数据书单】助你迎接职场新春天!
  • 2020产品经理升职攻略-直播分享课 第三期!
  • 月薪30k-50k、面试通过率90%,这个职位到底是在做什么?
  • 图灵奖公布!计算机图形学先驱Hanrahan和Catmull获奖,祝贺两位好莱坞3D动画大片技术元老...
  • 50亿美元代价的背后,企业该如何做好数据安全?
  • MySQL 狠甩 Oracle 稳居 Top1,私有云最受重用,大数据人才匮乏!
  • 【直播预告】3月22日|数据分析师成长之路
  • “Flutter中文网”社区创始人倾力撰写的网红书 《Flutter实战》正式出版
  • 不用掉一根头发!用 Flutter + Dart 快速构建一款绝美移动 App
  • 从0到1构建用户画像系统技术和方法论
  • 系统能力培养理念在人工智能领域的体现:打通系统与算法,培养系统思维的《智能计算系统》...
  • 武大学生用Python敲出樱花开放 | 附源码
  • VR-AR应用如何改变我们的城市生活?
  • 一场漫步园林的视听盛宴
  • classpath对获取配置文件的影响
  • cookie和session
  • open-falcon 开发笔记(一):从零开始搭建虚拟服务器和监测环境
  • 基于HAProxy的高性能缓存服务器nuster
  • 将 Measurements 和 Units 应用到物理学
  • 巧用 TypeScript (一)
  • 移动互联网+智能运营体系搭建=你家有金矿啊!
  • 做一名精致的JavaScripter 01:JavaScript简介
  • 昨天1024程序员节,我故意写了个死循环~
  • #android不同版本废弃api,新api。
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #LLM入门|Prompt#3.3_存储_Memory
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • (145)光线追踪距离场柔和阴影
  • (3)STL算法之搜索
  • (C语言)输入一个序列,判断是否为奇偶交叉数
  • (poj1.3.2)1791(构造法模拟)
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (仿QQ聊天消息列表加载)wp7 listbox 列表项逐一加载的一种实现方式,以及加入渐显动画...
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (数位dp) 算法竞赛入门到进阶 书本题集
  • (四)TensorRT | 基于 GPU 端的 Python 推理
  • (转)平衡树
  • .naturalWidth 和naturalHeight属性,
  • .net core 6 使用注解自动注入实例,无需构造注入 autowrite4net
  • .net 获取url的方法
  • .Net开发笔记(二十)创建一个需要授权的第三方组件
  • .net生成的类,跨工程调用显示注释
  • .Net转前端开发-启航篇,如何定制博客园主题
  • 。Net下Windows服务程序开发疑惑
  • /etc/shadow字段详解
  • /proc/vmstat 详解
  • ??eclipse的安装配置问题!??
  • @for /l %i in (1,1,10) do md %i 批处理自动建立目录
  • [ Linux 长征路第五篇 ] make/Makefile Linux项目自动化创建工具
  • [2013][note]通过石墨烯调谐用于开关、传感的动态可重构Fano超——
  • [2019/05/17]解决springboot测试List接口时JSON传参异常
  • [202209]mysql8.0 双主集群搭建 亲测可用
  • [codeforces]Levko and Permutation
  • [error] 17755#0: *58522 readv() failed (104: Connection reset by peer) while reading upstream