OpenCV Android开发实战(txt+pdf+epub+mobi电子书下载)


发布时间:2021-08-03 04:14:09

点击下载

作者:贾志刚

出版社:机械工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

OpenCV Android开发实战

OpenCV Android开发实战试读:

前言

为什么要写这本书

2015年,我出版了第一本图像处理方面的图书《Java数字图像处理:编程技巧与应用实践》,该书主要讲述图像处理的各种基础算法原理与代码实现,基于Java语言进行描述,没有太多的工程应用实践案例,是一本编程实践入门级的图像处理图书。因此我一直想再写一本工程实践性比较强的图书,Java与Android程序员可以通过这样的书籍,摆脱底层算法实现难的烦恼,快速学习相关API的使用,掌握常见的图像处理技术,快速开发应用,上手计算机视觉应用开发;他们通过学习与参照书中的工程实践案例,可以解决实际需求,提升个人竞争力,为企业和个人在短时间内创造更大的价值。

OpenCV作为一款开源的计算机视觉框架,封装了超过1000个常见的图像处理算法,其SDK语言支持Java、C++、Python等。借助人工智能兴起的东风,近几年OpenCV开发者社区的发展非常迅速,人数成几何级递增,而且已经对Android系统有了良好的支持与完备的SDK开发接口。在无须了解底层算法实现的情况下,借助OpenCV提供的SDK,Android开发者可以实现OCR识别、图像处理、人脸检测、相机校正、实时视频分析与处理、AR增强等移动端应用开发。

对大多数Android开发者来说,OpenCV与计算机视觉应用开发都可能显得有点陌生,因为市面上缺乏专业的工程性书籍与文档,OpenCV社区对Android SDK本身也没有提供完善的API文档与代码演示,这让很多Android程序员无法顺利使用OpenCV框架在移动端开发计算机视觉相关的应用。本书系统性地讲述OpenCV如何在Android系统上应用开发与工程实践,撰写本书的时候,因为OpenCV的很多API调用参数缺少文档说明,因此笔者需要通过编程实践一点一点全部尝试之后再总结出来,用实践出真知来形容本书一点也不过分。笔者本人是个地道的程序员,特别理解和了解程序员的视角与工程应用的重点和难点,本书从程序员的视角出发,在思路分析与代码实现上,对每个案例都做了非常清楚的交代与解释,对不同算法函数的应用场景都有详细的代码演示。本书最后三个案例分别涉及OCR识别、人脸美颜算法、视频检测与跟踪渲染这些实际落地场景,这三个案例是笔者本人精心挑选的,涵盖了大多数Android开发者的工程实践需求与工作需要,力求做到尽善尽美,然“人无完人,金无足赤”,最终还需读者评价。

如果说我的第一本书是对我十年工作的总结,那么本书就是我十年之后再出发的征途起点,“远飞者当换其新羽”,对广大Android与Java程序员来说,处在人工智能时代,掌握前沿技术,更新自己的技术栈,提升个人竞争力,计算机视觉与OpenCV就是个很好的方向与选择。作为技术人员唯有鼎故革新、砥砺前行,才能不负这个最好的时代,本书也是献给广大Android与Java程序员最好的礼物。

最后,希望通过本书的知识和作者有限的经验,帮助广大Android与Java程序员,以及众多有志于从事计算机视觉的后来者,借助OpenCV框架走上计算机视觉应用开发的道路。本书的顺利出版离不开笔者对OpenCV与计算机视觉技术的兴趣,更离不开笔者的毅力与本书写作初衷。希望本书能为国内OpenCV框架使用的普及与应用开发实践尽绵薄之力,若能如愿也不枉我的一番努力。读者对象

本书适合于以下读者对象。

·广大Android与Java程序员。

·从事图像处理的工作者。

·学习图像处理的爱好者。

·希望提升自我的中高级程序员。

·计算机专业高年级本科生或者研究生。

·从事图像处理行业的公司与个人。

·开设图像处理相关课程的大专院校学生。如何阅读本书

本书共分为两大部分,其中第一部分为第1章到第7章,系统地介绍了OpenCV Android的开发框架及功能。第二部分是本书的案例部分,系统全面地分析了三个实际案例,讲解如何借助OpenCV框架解决实际问题。如果你已经对Java语言和Android系统上的SDK开发有基本的认识,那么可以直接开始阅读本书,书中的源代码也是本书的一部分,建议在阅读本书内容的同时,尝试运行与修改本书提供的源代码,这样有助于更加深刻地理解与之相关的API参数与算法应用场景。

第一部分为基础篇,由浅入深,从OpenCV框架的简单介绍到OpenCV与Android SDK、NDK的编程应用,系统全面地介绍了OpenCV在移动领域的应用、OpenCV中的核心模块、图像处理模块、特征提取与对象检测模块等。读者在学习与掌握OpenCV相关API用法的前提下可以学习第二部的实战案例。

第二部分为实战案例部分,由OCR识别、人脸美颜、人眼实时跟踪与渲染三个典型案例组成。通过案例学习,读者将学会如何设计算法流程、使用组合算法API、关注应用的性能与内存问题,以及NDK开发技巧、其他图像处理开发相关API的使用技巧。

此外,本书的源文件可到www.hzbook.com上搜索本书书名下载,或者到Github上下载本书演示工程,网址为https://github.com/gloomyfish1998/opencv4android/tree/master/samples/OpencvDemo。

本书参考资料也可从Github上下载,网址为https://github.com/gloomyfish1998/opencv4android。勘误和支持

由于笔者的水平有限,编写的时间也很仓促,书中难免会出现一些错误或者不准确的地方,不妥之处在所难免,恳请读者批评指正。笔者已经把本书配套的源代码上传到Github,访问地址为https://github.com/gloomyfish1998/opencv4android/tree/master/samples/OpencvDemo,如果有读者想直接提交勘误代码,请先邮件联系笔者,笔者同意以后即可提交,同时笔者也会根据读者反馈更新源代码,所以在阅读本书之前请先从Github上获取最新的配套源代码。如果你有更多的宝贵意见,也欢迎发送邮件至邮箱57558865@qq.com,很期待听到你们的真挚反馈。致谢

OpenCV能有今天的发展,首先要感谢英特尔当时的开源决策,其次是OpenCV社区的巨大贡献,我第一次接触OpenCV就被它的开发效率吸引住了,可以说OpenCV是计算机视觉应用开发最好用的工具之一,特别是OpenCV3.0以后的版本,非常容易学习,所以要感谢那些为OpenCV做出过贡献的杰出开发者。在我写作本书的时候,机械工业出版社华章公司的编辑杨绣国老师一直没有向我催稿,反而告诉我要安心创作,认真细致,后期审稿的时候也是逐字逐句推敲,反复修改,感谢你的耐心与严谨,正是你的鼓励、帮助和支持引导我顺利完成本书撰写。

最后感谢我的爸爸、妈妈,感谢你们给予我生命,将我培养成人,感谢我的妻子在我写书的这一年多时间里让我从家务中解脱,给予我支持与鼓励。

谨以此书,献给我最亲爱的两个孩子,以及众多热爱OpenCV编程的朋友。贾志刚中国,苏州,2018年3月第一部分OpenCV图像处理系统学习篇

第一部分为基础篇,由浅入深、从OpenCV框架的简单介绍到OpenCV与Android SDK、NDK的编程应用、系统全面地介绍了OpenCV在移动领域的应用、OpenCV中的核心模块、图像处理模块、特征提取与对象检测模块等。读者在学习与掌握OpenCV相关API用法的前提下可以开始学习第二部的实战案例。第1章OpenCV Android开发框架

在开始本书内容之前,笔者假设大家已经有了面向对象语言编程的基本概念,了解了Java语言的基本语法与特征,并且尝试过Android平台上的应用程序开发。本章将主要介绍OpenCV的历史与发展、各个模块的功能说明、如何使用Android Studio IDE来建立OpenCV的开发环境,以及如何整合配置并成功运行和调用OpenCV中的函数实现一个最简单的OpenCV程序演示。如果没有特别说明,那么这里使用的OpenCV版本都是基于OpenCV 3.3 Android SDK。

作为使用最为广泛的计算机视觉开源库,OpenCV在开源社区与英特尔、谷歌等大公司的共同努力之下,发展到今天,已经吸引了全世界各地的开发者编译和使用它实现各种应用程序。而伴随着人工智能时代的到来,作为人工智能眼睛的计算机视觉必然会进一步释放活力,满足市场需要。OpenCV作为计算机视觉开源框架,其在移动端支持Android系统的特性必将进一步深入到移动开发的各种应用场景之中,下面就来开启一段OpenCV学习旅程。1.1 OpenCV是什么

OpenCV的中文全称是源代码开放的计算机视觉库(Open Source Computer Vision Library),是基于C/C++编写的,是BSD开源许可的计算机视觉开发框架,其开源协议允许在学术研究与商业应用开发中免费使用它。OpenCN支持Windows、Linux、Mac OS、iOS与Android操作系统上的应用开发。在笔者动笔写这本书的时候,其最新版本3.3刚刚发布不久。1.1.1 OpenCV的历史与发展

在OpenCV孕育发展的过程中,Intel公司做出了巨大的贡献,OpenCV最初是Intel公司的内部项目,随着时间的推移、OpenCV的功能算法得到不断的优化与增强,不过是短短十几年的时间,其已经席卷整个业界,得到众多著名IT公司的大力支持,其中包括大名鼎鼎的机器人公司Willow Garage与搜索引擎公司Google。下面的时间节点对OpenCV的发展都产生过重要影响,具体如下。

·1999年,OpenCV正式立项,那个时候Android智能手机的春天还没有到来。

·2000年,在IEEE的计算机视觉与模式识别大会上OpenCV正式发布Alpha版本。

·2001年~2005年,Intel公司陆续发布了最初的5个Beta测试版本。

·2006年,OpenCV1.0版本正式发布,基于C语言接口SDK调用。

·2008年,OpenCV获得了当时发展如日中天的机器人公司Willow Garage的支持、得到了进一步推广,然而不幸的是,作为机器人业界的传奇公司Willow Garage却在2014宣布倒闭。

·2009年,OpenCV2.0版本正式发布,这是OpenCV发展史上的一个重要里程碑,早期的OpenCV是基于C语言实现的,在2.0的版本中又添加了C++接口,并且对原来的C语言代码进行了优化和整合,以期吸引更多的开发者用户。

·2012年,Intel公司决定把OpenCV开发者社区正式交给开源社区opencv.org运营与维护。

·2014年,OpenCV3.0版本发布。

·2016年,OpenCV3.1与OpenCV3.2版本相继发布,其扩展模块支持集成Google TensorFlow深度学习框架。

·2017年,OpenCV3.3.x版本发布,在Release开发包中增加了DNN(深度神经网络)模块支持。

OpenCV支持Java语言开发的Android SDK最早是始于2010年。在OpenCV3.x版本中,OpenCV更加强调对移动端与嵌入式设备的支持。(1)编程语言

OpenCV中的这些模块大多数都是基于C/C++完成的,少量的SDK接口模块使用Java、Python等语言开发。在最新开发的OpenCV的核心模块中,C++替代C成为了开发语言。(2)应用领域

OpenCV自从1.0版本发布以来,立刻吸引了许多公司的目光,被广泛应用于许多领域的产品研发与创新上,相关应用包括卫星地图与电子地图拼接,医学中图像噪声处理、对象检测,安防监控领域的安全与入侵检测、自动监视报警,制造业与工业中的产品质量检测、摄像机标定,军事领域的无人机飞行、无人驾驶与水下机器人等众多领域。1.1.2 OpenCV模块介绍

OpenCV分为正式的发布版本与扩展模块,Android SDK所对应的是OpenCV的发布版本,其扩展模块的功能可以通过源代码编译的方式进行集成与开发,关于扩展模块的编译与使用已经超出了本书的讨论范围,这里就不再赘述了。下面以OpenCV3.3为例,OpenCV正式发布版本中包含的核心功能模块具体如下。

·二维与三维特征工具箱

·运动估算

·人脸识别

·姿势识别

·人机交互

·运动理解

·对象检测

·移动机器人

·分割与识别

·视频分析

·运动跟踪

·图像处理

·机器学习

·深度神经网络

除上所述的核心功能模块之外,其扩展模块更加的庞大与繁杂。OpenCV Android SDK可以从其官方主页上下载获得,下载地址为:http://opencv.org/opencv-3-3.html,在最下面就可以发现Android SDK的下载链接,点击就可以直接去相关页面上下载最新的Android SDK。1.1.3 OpenCV Android SDK

OpenCV Android SDK本质上是使用Java语言接口通过JNI技术调用OpenCV C/C++代码完成的算法模块。OpenCV4Android本身并不是一个纯Java语言的计算机视觉库,而是基于OpenCVC++本地代码、通过Java语言接口定义,基于JNI技术实现调用C++本地方法的SDK开发包。所以当你下载好OpenCV Android SDK之后,在它的SDK目录下可以看到如图1-1所示的目录结构。图 1-1

其中,etc目录里面有两个文件夹,里面都是一些XML数据文件,这些XML数据是训练好的HAAR与LBP级联分类器数据;java目录里面是Android SDK相关文件;native里面则是基于C/C++编译好的OpenCV Android平台支持的本地库文件、JNI层开发所需要的头文件及cmake文件,其中库文件大多数以*.a和*.so结尾。而在与SDK同层级的samples目录中则包含了OpenCV Android SDK的一些应用案例教程,以供初学者参考,但是很不幸的是,直到今天为止,这些教程仍然还是基于Eclipse开发环境来演示OpenCV功能,不得不说这是一个小小的缺憾,希望OpenCV社区在后面的Open CV版本中能够更新这些教程,使其基于Android Studio来演示。

此外,OpenCV Android SDK的功能与OpenCV对应发布版本中的功能完全相同,唯一不同的是因为Java语言的关系,Java层封装的接口的参数传递和方法调用,与C++的接口相比有一些差异,这些都是为了更适应Java语言的特性而做出的改动,使得Android开发者更加容易学习与使用OpenCV来解决问题。1.2 OpenCV Android开发环境搭建

当OpenCV遇到Android时,两者就通过Java SDK或者Android NDK很好地结合在一起了,可是对于广大Android开发者或者OpenCV开发者来说,要想成功地在Android Studio上运行一个类似于Hello World的OpenCV程序,还需要做一些工作,下面就一起来完成这些工作,实现开发环境的搭建。1.2.1 软件下载与安装

在搭建开发环境之前,首先需要下载和安装如下几个软件开发包。

·OpenCV Android SDK 3.3版本

·JDK8:64位

·Android Studio

·Android SDK与NDK开发包

这里需要特别说明一下的是,首先应该安装好JDK,之后再下载安装其他的软件开发包,全部下载安装完毕之后,就可以打开Android Studio——Android集成开发环境(IDE),配置好Android SDK的路径之后,Android Studio(IDE)工具就可以正常使用了。

所下载的OpenCV Android SDK 3.3是一个安装包,只要解压缩到指定磁盘即可,双击解压缩好的目录就可以看到1.1.3节中提到的几个目录与层次结构。在本书最后的案例开发中会涉及与使用NDK的开发包,这里暂时只要将其安装好即可。

版本问题

使用Android Studio与Android SDK、NDK开发时,同样的代码在不同的版本上运行可能会出现一些兼容性问题。因为项目实际需要或者个人偏好,大家使用的版本可能不尽相同,这里说一下本书所用的版本,具体如下。

·Android Studio 3.0

·Android SDK 26

·Android NDK r13b1.2.2 环境搭建

环境搭建的整个过程可以分为如下四步。

1.新建Android项目

打开Android Studio IDE,选择【File】→【New Project...】,结果如图1-2所示。

把项目默认名称修改为OpencvDemo,然后点击【Next】按钮,结果如图1-3所示。

这里支持的最小版本是Android 14(Android 4.0版本),继续点击【Next】按钮,结果如图1-4所示。

默认选择Emtpy Activity,点击【Next】→【Finish】,之后就会得到一个新建的默认Android版本的Hello World程序,如果一切顺利的话,就可以真机运行,查看效果。图 1-2图 1-3图 1-4

2.导入OpenCV Android SDK依赖项

选择【File】→[New...]→【Import Module...】,打开对话框之后,选择解压缩好的OpenCV Android SDK目录中的sdk\java,模块名称会自动显示出当前OpenCV的版本信息,如图1-5所示。

点击【Next】→【Finish】,完成导入。然后再选择【File】→【Project Structure...】打开依赖项添加对话框,选择最右侧的【+】按钮,完成添加之后如图1-6所示。

点击【OK】按钮,结束。

3.复制本地依赖项OpenCV库文件

把目录结构导航从【Android】切换到【Projects】,如图1-7所示。图 1-5图 1-6图 1-7

选择app下面的libs,然后把OpenCV Android SDK目录native\libs下面的所有文件与文件夹全部复制到libs中去,最后删除所有以*.a结尾的文件。

4.修改Gradle脚本与编译

在Android Studio中双击打开如下两个Gradle脚本,如图1-8所示。

把两个脚本中的minSdkVersion修改为14、targetSdkVersion修改为26,然后保存,如图1-9所示。图 1-8图 1-9

在Module:app对应的build.gradle脚本中添加如下内容:task nativeLibsToJar(type: Jar, description: 'create a jar archive of the native libs') { destinationDir file("$buildDir/native-libs") baseName 'native-libs' from fileTree(dir: 'libs', include: '**/*.so') into 'lib/'}tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn(nativeLibsToJar)}

然后在编译片段添加如下代码:implementation fileTree(dir: "$buildDir/native-libs", include: 'native-libs.jar')

保存最终修改好的Gradle文件即可。选择【build】→【clean project】,之后再选择【rebuild project】就完成了整个环境变量的配置与编译。可是环境变量配置得是否正确,我们还不能做到心中有数,所以下面通过一个简单的测试程序来验证一下环境配置。1.2.3 代码测试

为了验证Android Studio的环境配置是否正确,需要调用一下OpenCV的相关API,把一张彩色图像转换为灰度图像,借此来验证Android平台上的OpenCV SDK是否可以正确调用。因此首先要实现图像显示,在Android中,可以通过XML(activity_main.xml)配置文件选择ImageView元素来实现,还需要一个按钮来响应用户操作,这可以通过添加Button元素来实现。在RelativeLayout中,两个元素对应的XML显示如下:

在代码层面实现图像资源的加载,然后交给OpenCV处理,之后的返回结果显示可以由如下3个步骤来实现。

1)首先加载OpenCV的本地库,代码如下:private void iniLoadOpenCV() { boolean success = OpenCVLoader.initDebug(); if(success) { Log.i(CV_TAG, "OpenCV Libraries loaded..."); } else { Toast.makeText(this.getApplicationContext(), "WARNING: Could not load OpenCV Libraries!", Toast.LENGTH_LONG).show(); }}

2)对按钮加上OnClickListener事件响应,代码如下:Button processBtn = (Button)this.findViewById(R.id.process_btn);processBtn.setOnClickListener(this);

3)在事件响应方法进行处理并显示结果,代码如下:@Overridepublic void onClick(View v) { Bitmap bitmap = BitmapFactory.decodeResource( this.getResources(), R.drawable.lena); Mat src = new Mat(); Mat dst = new Mat(); Utils.bitmapToMat(bitmap, src); Imgproc.cvtColor(src, dst, Imgproc.COLOR_BGRA2GRAY); Utils.matToBitmap(dst, bitmap); ImageView iv = (ImageView)this.findViewById(R.id.sample_img); iv.setImageBitmap(bitmap); src.release(); dst.release();}

需要注意的是,在MainActivity中,需要完成接口View.OnClickListener,这样才能保证按钮事件的正确响应。可以在手机上看到运行完整代码的效果。

注意:书中所有完整的源代码都可以在Github上下载,强烈建议运行每个源代码实例,将源代码看作本书的一部分。1.3 构建演示APP

本节将尝试构建一个用来演示本书所讲内容的APP,希望在其中可以按章节来索引各章节的相关功能演示。在UI设计层面,首先需要设计一个流程,实现从主界面选择各章,然后到对应的各节的代码演示。该流程如图1-10所示。图 1-10

根据上述的流程可知,我们所需要的界面功能与元素如表1-1所示。表 1-1

根据表1-1所述的UI设计与程序流程,在启动程序之后,首先选择所在章节,然后选择相关的演示程序进行查看,这样的APP结构有利于集成每章相关的演示程序。根据1.2节的内容,我们首先需要在layout目录下创建两个XML文件作为界面2与界面3,然后还需要将activity_main.xml文件中添加的ImageView与Button元素移到界面3中,在activity_main.xml中添加一个ListView元素作为界面1。这一切做好之后,再来看一下activity_main.xml中的ListView XML显示:

做好了XML界面编程之后,需要创建如图1-11所示的几个类与包,它们之间的关系通过类图显示。

图1-11中各个类的功能说明具体如下。

·MainActivity:第一个界面,显示本书的10个章标题列表。

·SectionsActivity:第二个界面,显示各章对应的演示程序。

·CharpteFrist1Activity:第三个界面,这里是第1章的演示程序,以后各章会创建属于自己的演示程序。

·ItemDto:列表的每个条目对应的数据类。

·ChapterUtils:工具类,可以获取章节列表。

·AppConstants:常量接口,定义各个章节与演示程序的名称。

·SectionsListViewAdaptor:Android ListView元素对应的数据模型。图 1-11

首先要实现ListView的显示且选择事件响应,在事件响应中实现View跳转到指定页面。其中ListView的初始化代码如下:private void initListView() { ListView listView = (ListView) findViewById(R.id.chapter_listView); final SectionsListViewAdaptor commandAdaptor = new SectionsListViewAdaptor (this); listView.setAdapter(commandAdaptor); commandAdaptor.getDataModel().addAll(ChapterUtils.getChapters()); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { ItemDto dot = commandAdaptor.getDataModel().get(position); goSectionList(dot); } }); commandAdaptor.notifyDataSetChanged();}

以第一个界面到第二个界面的跳转为例,实现跳转的代码如下:private void goSectionList(ItemDto dto) { Intent intent = new Intent(this.getApplicationContext(), SectionsActivity.class); intent.putExtra(AppConstants.ITEM_KEY, dto); startActivity(intent);}

实现从第二个Activity(SectionActivity)跳转到各个演示程序的功能,首先需要在onCreate方法中对每章内容的演示程序实现ListView显示与选择监听,这部分的代码如下:private void initListView(ItemDto dto) { ListView listView = (ListView) findViewById(R.id.secction_listView); final SectionsListViewAdaptor commandAdaptor = new SectionsListViewAdaptor (this); listView.setAdapter(commandAdaptor); commandAdaptor.getDataModel().addAll(ChapterUtils.getSections((int)dto.getId())); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { String command = commandAdaptor.getDataModel().get(position).getName(); goDemoView(command); } }); commandAdaptor.notifyDataSetChanged();}

然后在goDemoView方法中根据章节内容跳转到不同的演示程序Activity中,跳转到第1章的演示程序代码如下:if(command.equals(AppConstants.CHAPTER_1TH_PGM_01)) { Intent intent = new Intent(this.getApplicationContext(), CharpteFrist1Activity.class); startActivity(intent);}

以后针对各章的内容,在此处添加相关的代码即可,这样就实现了代码的集成。本章完整的源代码可以参见上述提到的源代码文件。1.4 拍照与图像选择

1.3节中,我们成功地构建了一个演示本书内容的APP框架,这里还需要对其进行进一步的细化,因为我们发现该框架还没有拍照或者图像选择功能,无法提供测试图像来演示OpenCV代码的功能,所以本节在1.3节所示代码的基础之上,再加上拍照与图像选择的功能。在Android系统中显示图像,早期一直有一个很大的问题,尤其是对于大的Bitmap对象,常常会因为DVM内存的问题导致OOM(Out of Momery)错误,开玩笑地说,做Java与Android开发如果没有遇到类似的问题,你出门都不好意思跟人打招呼。谷歌官方的做法是通过降采样使用Bitmap的微缩版图像,这里对选择的图像或者拍照所得图像的显示与处理依然沿用此策略。

1.拍照

调用Android拍照功能,拍照并返回图像,代码如下:Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);fileUri = Uri.fromFile(getSaveFilePath());intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);startActivityForResult(intent, REQUEST_CAPTURE_IMAGE);

2.图像选择

调用Android图片浏览功能,选择一张图片,自动返回图像并显示,代码如下:Intent intent = new Intent();intent.setType("image/*");intent.setAction(Intent.ACTION_GET_CONTENT);startActivityForResult(Intent.createChooser(intent, "图像选择..."), REQUEST_CAPTURE_IMAGE);

3.加载大小合适的图像

首先获取图像的大小,然后得到图像的降采样版本,显示在ImageView对象元素中,杜绝OOM问题的发生,代码如下:if(fileUri == null) return;ImageView imageView = (ImageView)this.findViewById(R.id.sample_img);BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeFile(fileUri.getPath(), options);int w = options.outWidth;int h = options.outHeight;int inSample = 1;if(w > 1000 || h > 1000) { while(Math.max(w/inSample, h/inSample) > 1000) { inSample *=2; }}options.inJustDecodeBounds = false;options.inSampleSize = inSample;options.inPreferredConfig = Bitmap.Config.ARGB_8888;Bitmap bm = BitmapFactory.decodeFile(fileUri.getPath(), options);imageView.setImageBitmap(bm);

4.处理与显示

调用OpenCV4Android的API对图像进行有目的的处理,处理之后返回处理后的图像并显示。这里我简单地改写一下前面的OpenCV测试程序,使用OpenCV的imread功能来读取图像,完成灰度转换并显示,代码如下:Mat src = Imgcodecs.imread(fileUri.getPath());if(src.empty()) { return;}Mat dst = new Mat();Imgproc.cvtColor(src, dst, Imgproc.COLOR_BGR2GRAY);Bitmap bitmap = grayMat2Bitmap(dst);ImageView iv = (ImageView)this.findViewById(R.id.sample_img);iv.setImageBitmap(bitmap);src.release();dst.release();

在拍照或者选择图像结束之后,回调onActivityResult()方法,对于照片,可以直接获得文件路径,而对于选择图像,则需要在回调处理中根据图像ID得到相关图像的正确文件路径。此外,这里还涉及后续章节中要详细解释的Mat对象与Bitmap对象。

本书后续章节的每个演示程序基本上都是基于这个顺序来实现的。而且如果没有特别的说明与支持,那么本书所有的图像都是RGB色彩空间、相关的拍照与图像选择代码会在本书后续章节中重复使用,后续章节针对这部分代码将不再重复讲述,主要的篇幅会放在OpenCV4Android相关知识的讲解与学习上。

注意:这里把加载OpenCV库文件放到了主界面对应的onCreate方法中去调用,这样就避免了到处加载OpenCV库文件的烦恼,同时有助于减少程序的代码量。1.5 小结

本章主要介绍了OpenCV的历史与发展、相关功能模块、开发环境搭建,以及为了后续更好地学习本书知识所做的一些必要的Android知识铺垫、测试程序框架设计与编码实现。有了这些基础,读者才可以更好地学习后续章节所讲的每个知识点,并通过程序与代码来演示应用。

同时,通过本章的学习,即使是之前没有接触过OpenCV与Android编程的读者也对二者会有基本的感性认知,对OpenCV、Android SDK、如何集成配置Android Studio、如何开发与运行代码都有一定的了解与接触,为后续熟练使用IDE来开发使用OpenCV4Android打下牢固基础。希望大家通过本章的学习,可以顺利搭建好开发环境,运行好测试程序,为后续学习打下坚实基础。第2章Mat与Bitmap对象

第1章中,我们介绍了OpenCV框架的历史与发展,基于Android Studio配置好了OpenCV Android SDK的开发环境,并尝试运行了我们第一个OpenCV相关的验证程序,同时为了更好地学习后续章节,搭建了一个简单的APP框架。本章将会学习OpenCV4Android中最重要的图像容器Mat的各种使用方法,以及如何在Mat上绘制各种几何形状,同时我们还会关注Android平台本身提供的图像对象Bitmap,重点解释Mat与Bitmap之间的区别与联系,使用时候的注意事项,以及它们之间的相互转换。

在介绍本章内容之前,笔者假设大家已经掌握了Android平台编程的基本知识,学习过简单的Android应用程序开发,同时对图像文件的格式及其特点有一些简单的了解。虽然这些知识点不是本章的重点,但是还是可以帮助你更好地学习本章内容。2.1 Mat对象

Mat是OpenCV中用来存储图像信息的内存对象,当通过Imgcodecs.imread()方法从文件读入一个图像文件时,imread方法就会返回Mat对象实例,或者通过Utils.bitmatToMat()方法由Bitmap对象转换而来。图2-1形象地展示了一张图像中的各个像素点数据是如何存储的,因为图像本身的像素点比较多,所以我们显示的图像像素数据只是左上角20×20大小的部分数据,具体显示如下。图 2-1

Mat对象中除了存储图像的像素数据以外,还包括图像的其他属性,具体为宽、高、类型、维度、大小、深度等。当你需要这些信息时,可以通过相关的API来获取这些基本图像属性。2.1.1 加载图像与读取基本信息

当我们从Android系统中选择一张图像的时候,我们可以使用如下代码将图像文件加载为Mat对象:Mat src = Imgcodecs.imread(fileUri.getPath());

OpenCV通过imread来加载图像,默认的时候,加载的是三通道顺序为BGR的彩色图像,还可以通过以下代码来指定加载为彩色图像:Mat src = Imgcodecs.imread(fileUri.getPath(), Imgcodecs.IMREAD_COLOR);

第一个参数表示文件路径,第二个参数表示加载图像类型,最常见的类型有如下几种。

·IMREAD_UNCHANGED=-1,表示不改变加载图像类型,可能包含透明通道。

·IMREAD_GRAYSCALE=0,表示加载图像为灰度图像。

·IMREAD_COLOR=1,表示加载图像为彩色图像。

使用如下代码从Mat对象中得到图像的宽、高、维度、通道数、深度、类型信息:int width = src.cols();int height = src.rows();int dims = src.dims();int channels = src.channels();int depth = src.depth();int type = src.type();

其中,要特别关注的是通道数、图像深度与图像类型、OpenCV加载的Mat类型图像对象。常见的通道数目有1、3、4,分别对应于单通道、三通道、四通道,其中四通道中通常会有透明通道的数据。图像深度表示每个通道灰度值所占的大小,图像深度与类型密切相关。OpenCV中常见的几种图像深度具体如表2-1所示。表 2-1

其中,U表示无符号整型、S表示符号整型、F表示浮点数,这些类型在CvType中可以自己查看。OpenCV中常见的图像类型具体如表2-2所示。表 2-2

当我们调用imread函数时,如果只使用文件路径参数读入加载一张图像,那么它的默认值是三通道的CV_8UC3,图像深度为CV_8U,其中CV表示计算机视觉、8表示八位、UC表示无符号char、3表示三个通道。在表2-2所示的类型表中,每个类型都可以做类似的解读,从此处也可以看出CV_8U就是图像深度,所以图像类型与深度之间是有直接关系的。以上就是对图像的加载与基本信息获取的解释,下面将介绍如何创建Mat对象以及初始化。2.1.2 Mat创建与初始化

从前面的内容我们可以知道,Mat对象中包含了图像的各种基本信息与图像像素数据。总的来说,Mat是由头部与数据部分组成的,其中头部还包含一个指向数据的指针。在OpenCV4Android的接口封装中,因为Java层面没有指针对象,因此全部用数组来替代。但是,当我们需要把Mat对象传到JNI层的时候,可以通过getNativeObjAddr()方法来实现Mat对象从Java层到C++层的指针传递,这点在后续的NDK编程中会有具体描述的示例代码,这里就不再展开说明了。如图2-2所示的是Mat在内存中的结构。

创建Mat对象的方法有很多种,其中最常见的有如下几种。图 2-2

1)通过create方法实现Mat对象的创建,相关的代码如下:Mat m1 = new Mat();m1.create(new Size(3, 3), CvType.CV_8UC3);Mat m2 = new Mat();m2.create(3, 3, CvType.CV_8UC3);

上述代码表示创建两个Mat对象m1与m2,其中m1与m2的大小都是3×3、类型是三通道8位的无符号字符型。

2)通过ones、eye、zeros方法实现初始化创建,相关代码如下:Mat m3 = Mat.eye(3, 3,CvType.CV_8UC3);Mat m4 = Mat.eye(new Size(3, 3),CvType.CV_8UC3);Mat m5 = Mat.zeros(new Size(3, 3), CvType.CV_8UC3);Mat m6 = Mat.ones(new Size(3, 3), CvType.CV_8UC3);

上述代码创建了m3、m4、m5、m6四个Mat对象,基于这种初始化方式来得到Mat对象是OpenCV借鉴了Matlab中eye、zeros、ones三个函数实现的。

3)此外还可以先定义Mat,然后通过setTo的方法实现初始化,相关代码如下:Mat m7 = new Mat(3, 3, CvType.CV_8UC3);m7.setTo(new Scalar(255, 255, 255));

此方法与第一种方法有点类似,唯一不同的是,第一种方法通过create初始化的时候并没有指定颜色值。在OpenCV中,颜色向量通常用Scalar表示,这里Scalar(255,255,255)表示白色。

4)通过Mat的copyTo()与clone方法实现对象的创建,Mat中的克隆与拷贝方法会复制一份完全相同的数据以创建一个新Mat对,克隆相关代码如下:Mat m8 = new Mat(500, 500, CvType.CV_8UC3);m8.setTo(new Scalar(127, 127, 127));Mat cmat = image.clone();

拷贝的相关代码如下:Mat m8 = new Mat(500, 500, CvType.CV_8UC3);m8.setTo(new Scalar(127, 127, 127));Mat result = new Mat();m8.copyTo(result);2.1.3 Mat对象保存

创建好的Mat对象经过一系列的操作之后,就可以通过OpenCV4Android的imwrite函数直接将对象保存为图像,相关的演示代码如下:// 创建Mat对象并保存Mat image = new Mat(500, 500, CvType.CV_8UC3);image.setTo(new Scalar(127, 127, 127));ImageSelectUtils.saveImage(image);

其中,500表示图像的宽度与高度,最后一个参数声明图像是RGB彩色三通道图像、每个通道都是8位,第二行代码是指定图像的每个像素点、每个通道的灰度值为127。第三行代码是使用imwrite将图像保存到手机中的指定目录下,saveImage方法的相关代码如下:File fileDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "mybook");if(!fileDir.exists()) { fileDir.mkdirs();}String name = String.valueOf(System.currentTimeMillis()) + "_book.jpg";File tempFile = new File(fileDir.getAbsoluteFile()+File.separator, name);Imgcodecs.imwrite(tempFile.getAbsolutePath(), image);

上面的前几行代码是创建目录与文件路径,最后一行代码通过imwrite来实现文件的保存,保存图像的格式取决于文件路径为图像指定的扩展名类型。2.2 Android中的Bitmap对象

上一节我们介绍了Mat对象,其实在Android系统中也有一个与Mat对象相似的对象Bitmap。本节我们将介绍Bitmap,通过它可以获取图像的常见属性、像素数据,修改图像的像素数据,呈现出不同的图像显示效果,保存图像,等等。

1.图像文件与资源加载

在Android系统中,我们可以把给定图像的文件路径或者图像资源ID作为参数,通过调用相关的API来实现文件的加载,使其成为一个Bitmap实例对象。最常见的加载资源图像的代码如下:Bitmap bm = BitmapFactory.decodeResource(this.getResources(), R.drawable.lena);

加载图像文件的代码与前面第1章中提到的类似,为了避免OOM问题,我们首先应该获取图像的大小,然后根据图像大小进行适当的降采样,之后再加载为Bitmap对象即可,这里就不再赘述了。

2.读写像素

对Bitmap对象来说,我们首先可以通过相关的API实现图像的长、宽、配置信息查询,在Bitmap中,像素数据是最占内存的部分。根据长、宽与配置信息我们可以计算出图像像素的大小为多少,定义一个数组用于存储一次性读出的像素数组,也可以通过每次读取一个像素点的方式来循环读取。Bitmap获取图像宽、高与配置信息的接口代码如下:public final int getWidth()public final int getHeight()public final Config getConfig()

其中,Config是Java中的枚举类型,当前Android支持的Bitmap像素存储类型具体如下:Bitmap.Config.ALPHA_8;Bitmap.Config.ARGB_4444;Bitmap.Config.RGB_565;Bitmap.Config.ARGB_8888;

默认情况下,Bitmap是在RGB色彩空间。其中,A表示透明通道、R表示红色通道、G表示绿色通道、B表示蓝色通道。其中ALPHA_8表示该图像只有透明通道而没有颜色通道,是一张透明通道图像,这种图像通常会被用作mask图像。上述代码中的参数具体分析如下。

·ARGB_4444:表示每个通道占四位,总计两个字节,表示一个像素的图像。

·ARGB_8888:表示每个通道占八位,总计四个字节,表示一个像素的图像,这个是最常见的。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载