Exiv2 With XMP for Android

  • Exiv2 With XMP for Android

    • 摘要
    • 编译环境
    • 安装独立工具链
    • 编译Expat
    • 编译Exiv2
    • 编译JNI so库
    • 源码

摘要

最近在从事VR行业,工作中需要将VR相机拍摄的360图片被各大VR平台识别,比如Facebook、Google Photo、LINE等。这些平台都是遵循google定的一套VR协议,详细可参考链接https://developers.google.com/streetview/spherical-metadata?hl=zh-cn。这些信息都是保存到图片的XMP(XMP stands for “Extensible Metadata Platform”, an XML/RDF-based metadata format which is being pushed by Adobe. Information in this format can be embedded in many different image file types including JPG, JP2, TIFF, GIF, EPS, PDF, PSD, IND, INX, PNG, DJVU, SVG, PGF, MIFF, XCF, CRW, DNG and a variety of proprietary TIFF-based RAW images, as well as MOV, AVI, ASF, WMV, FLV, SWF and MP4 videos, and WMA and audio formats supporting ID3v2 information.)中,PC端有一些工具可以通过python脚本做处理,但是在Android端就很难找到现有的工具使用了。
一次偶然的机会,看到了Exiv2这个开源工具,能够支持XMP的读写,更可贵的是,最新版上已经默认支持google 360 metadata。

Exiv2是C++编写,支持交叉编译,在Ubuntu、Linux、Windows都可以直接下载编译好的版本进行使用。如果需要在Android端使用,就需要基于源码进行单独编译。


编译环境

  • Android NDK
  • Ubuntu 16.04 LTS
  • Exiv2 V0.26, 请下载trunk上的完整代码。http://www.exiv2.org/download.html
  • Expat V2.2.2。 https://libexpat.github.io/

安装独立工具链

您可以独立使用 Android NDK 附带的工具链,或将其作为插件与现有 IDE 结合使用。 如果您已有自己的构建系统,且仅需要调用交叉编译器功能以将对构建系统的支持添加到 Android,则这种灵活性非常有用。 —— [ Android Developer ]

安装命令如下:

./make-standalone-toolchain.sh --platform=android-21 --install-dir=~/my-android-toolchain --ndk-dir='${yourNdkPath}' --toolchain=arm-linux-androideabi-4.9

需要注意的是,Exiv2如果使用低于android-21的platform进行编译,会报crwimage.cpp中某个方法无法找到的错误。
安装独立工具链的好处是将编译所需要的所有文件都拷贝到一个文件夹下,能够在编译过程中更好的link到依赖的头文件和库文件。

编译Expat

Expat是一个用C语言开发的、用来解析XML文档的开发库,它最初是开源的、Mozilla 项目下的一个XML解析器。 —— [ 百度百科]

如文章开头所述,图片的XMP信息是基于xml的,所以需要有个xml解析器来做解析和生成。Exiv2中就是使用Expat来进行。XMP是Exiv2中一个独立功能,可以在编译选项中设置–disable-xmp来禁止编译该功能,默认是编译。编译带有xmp功能的exiv2时,若不设置expat,会在编译过程中提示设置expat-with=DIR的错误信息。
因此必须先编译出expat for android.
Expat是纯c写的,相对于exiv2,编译方式要简单,而且expat是android默认内置的动态库,可以到/system/libs或者libs32中查找。一开始为了快速编译exiv2,遍从手机里面将libexpat.so拷贝出来,放到对应路径,即能编译成功。这是下策,但是真有这么干的,见:https://stackoverflow.com/questions/16960523/how-to-use-internal-expat-xml-parser-in-android。
编译Expat的脚本内容如下:

#!/bin/bash
export INSTALL_DIR="`pwd`/jni/expat"
export NDK=/${yourNdkPath}
export SYSROOT=~/my-android-toolchain/sysroot
export TOOLCHAIN=~/my-android-toolchainexport CROSS_COMPILE="arm-linux-androideabi"
export PATH="$TOOLCHAIN/bin:$PATH"
export ARCH="armeabi-v7a"
export CC="${CROSS_COMPILE}-gcc --sysroot=$SYSROOT"
export LD="${CROSS_COMPILE}-ld --sysroot=$SYSROOT"
export CFLAGS="-D__ANDROID__ -D__ARM_ARCH_7__ -D__ARM_ARCH_7T__ -D__ARM_ARCH_7E__ -D__ARM_ARCH_7TE__ -mandroid --sysroot $SYSROOT -march=armv7-a -mfloat-abi=softfp -mthumb -O2"
export LDFLAGS='-s'mkdir -p $INSTALL_DIR
./configure --host=arm-linux-androideabi --prefix=$INSTALL_DIRmake
make installexit 0

–sysroot表示系统库的所在路径,该路径下通常包含usr/include, usr/lib两个文件夹。如果不加红字部分,在执行./buildexpat.sh时可能会提示c编译器执行失败(某些网上资料说也可能会默认使用独立编译链,所以会自动找到该文件夹,不会报错)。打开config.log,会发现cannot find -lc或者-ldl的error信息。
编译成功后,最后生成的文件可以在jni/expat/下找到,包括bin、include、lib、share四个文件夹,其中include和lib是编译exiv2所需要的文件。Include文件夹中包含expat.h、expat_config.h、expat_external.h三个头文件,lib中主要使用libexpat.a, libexpat.so。由于编译exiv2我们编译的是静态库,所以下面编译exiv2时使用的是libexpat.a。可以将include和lib两个文件夹拷贝到一个其它文件夹下,其中lib下只有libexpat.a,接着将这个文件夹路径在buildExiv2.sh中设置为—with-expat=”${yourExpatPath}”。

编译Exiv2

终于要编译Exiv2了。这才是整篇文章中最重要的一环。
虽然网上没有现有的例子能够直接编译出exiv2 for android,但还是有两篇帖子提到了,只不过发表于5 6年前,已经不适合最新的exiv2。两个链接的内容差不多:https://stackoverflow.com/questions/30647594/ndk-build-outputs-error-adding-symbols-file-in-wrong-format。本文就是基于这个链接内容操作的,stackoverflow真是牛,什么技术问题都能遇到。
下载完源码,开始编译吧。编译脚本如下:

#!/bin/bash
INSTALL_DIR="`pwd`/jni/exiv2"
export NDK=/${yourNdkPath}
export SYSROOT=~/my-android-toolchain/sysroot
export TOOLCHAIN=~/my-android-toolchain
export PATH=$TOOLCHAIN/bin:$PATH
export ARCH="armeabi-v7a"
export CROSS_COMPILE="arm-linux-androideabi"
export CC="${CROSS_COMPILE}-gcc --sysroot=$SYSROOT"
export CXX="${CROSS_COMPILE}-g++ --sysroot=$SYSROOT"
export CFLAGS="-D__ANDROID__ -D__ARM_ARCH_7__ -D__ARM_ARCH_7T__ -D__ARM_ARCH_7E__ -D__ARM_ARCH_7TE__ -mandroid --sysroot $SYSROOT -march=armv7-a -mfloat-abi=softfp -O2"export CXXFLAGS="$CFLAGS"
export LDFLAGS="-Wl,-rpath-link=$SYSROOT/usr/lib/ -L$SYROOT/usr/lib/ -L${NDK}/sources/cxx-stl/gnu-libstdc++/libs/armeabi-v7a"
export LIBS="-lstdc++ -lsupc++"mkdir -p $INSTALL_DIR
./configure --disable-shared --disable-nls --host=arm-linux-androideabi --with-expat=/${yourExpatLibPath} --prefix=$INSTALL_DIR make
make installexit 0

脚本中需要注意的就是前面提到的–with-expat,这个路径需要填写之前expat编译完成后包含include和lib的文件夹,为了安全起见,可以复制一份到其它路径,并且删除lib下的so库,仅适用.a文件。
执行shell脚本,如果能一次编译成功,那就谢天鞋地谢亚龙了,可惜有错。错误信息如下:

进入actions.cpp查看出错的代码,然后发现该变量应该定义到pthread.h。搜索ndk中出现的pthread.h,没有找到define的地方,只能怀疑在arm中没有定义。没定义就算了,就使用android里面基础的mutex。需要将static pthread_mutex_t cs = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,改成static pthread_mutex_t cs。

继续编译吧,可惜天不随人愿,又error了。

lpthread找不到?why?因为android不支持lpthread,那改成android支持的pthread就行。可是在哪里改?find命令可出场了。
计入exiv2源码的根目录,执行find . –name “*” | xargs grep “lpthread”,会列出很多文件都有lpthread,原因是已经执行过configure和make,会有些临时生成文件中也包含lpthread。比如.la文件。所以建议重新copy一份原始exiv2源码,然后进入这个文件夹进行搜索,会发现其实只有两个文件中包含lpthread,config/config.ac和configure文件。其中config.ac是帮助生成configure文件的。找到后,安全起见,把所有的lpthread改成pthread吧。改完后,记得将脚本和新修改的actions.cpp拷贝过来。再次执行编译脚本。
这次我编译成功了。

编译后的libexiv2放在脚本中设置的installDir中,里面和expat类似,至少包含lib和include两个文件夹。

编译JNI so库

东西是编译出来了,能用吗?虽然前面说的问题不是很多,但是在真正编译过程中遇到的问题很多,我编译出来的前两个版本都是不能用的。所以需要编译出jni的so库来验证。
按照创建jni的标准方式来创建一个新的project,内容主要包括:

其中include文件夹包含的expat编译出来的三个头文件和exiv2编译出来的所有头文件,lib下包含libexpat.a和libexiv2.a。libs是放置build结果的文件夹。
先看看Application.mk如何写吧。

NDK_TOOLCHAIN_VERSION=4.9
APP_ABI := armeabi-v7a
APP_CPPFLAGS += -fexceptions
APP_STL := gnustl_static
APP_CPPFLAGS += -frtti
APP_PLATFORM := android-21
APP_BUILD_SCRIPT := Android.mk

最后一句APP_BUILD_SCRIPT := Android.mk在配置了ndk application path下是可以去掉的。

Android.mk的内容如下:

LOCAL_PATH := $(call my-dir)#static library info
include $(CLEAR_VARS)
LOCAL_MODULE := exiv2
LOCAL_SRC_FILES := lib/libexiv2.a
LOCAL_EXPORT_C_INCLUDES := include/
LOCAL_EXPORT_LDLIBS := -Llib -lz -lexpat
include $(PREBUILT_STATIC_LIBRARY)#wrapper info
include $(CLEAR_VARS)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include/
LOCAL_MODULE    := xmp
LOCAL_SRC_FILES := exiv2Jni.cpp
LOCAL_STATIC_LIBRARIES := exiv2
include $(BUILD_SHARED_LIBRARY)

最重要的一句是LOCAL_EXPORT_LDLIBS := -Llib -lz -lexpat。不要忘了expat。因为exiv2是依赖expat的,如果没有这句话,在编译过程中,会报某些xml方法无法找到的错误。原因就是这些方法是在expat中被定义的。

最后在看看exiv2Jni.cpp的代码

#include <jni.h>
#include <string>#include "exiv2/exiv2.hpp"
#include <iostream>
using namespace std;extern "C"
JNIEXPORT void JNICALL
Java_com_xxxx_yyyyy_zzzzz_ExivJni_setImageGPano(JNIEnv *env, jobject instance, jstring path_, jint width, jint height) {const char *path = env->GetStringUTFChars(path_, 0);Exiv2::XmpData xmpData;xmpData["Xmp.GPano.UsePanoramaViewer"] = true;xmpData["Xmp.GPano.ProjectionType"] = "equirectangular";xmpData["Xmp.GPano.CroppedAreaImageWidthPixels"] = width;xmpData["Xmp.GPano.CroppedAreaImageHeightPixels"] = height;xmpData["Xmp.GPano.CroppedAreaLeftPixels"] = 0;xmpData["Xmp.GPano.CroppedAreaTopPixels"] = 0;xmpData["Xmp.GPano.FullPanoWidthPixels"] = width;xmpData["Xmp.GPano.FullPanoHeightPixels"] = height;Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path);image->setXmpData(xmpData);image->writeMetadata();cout << path << endl;env->ReleaseStringUTFChars(path_, path);
}

这里面不要忘记extern ‘C’。因为exiv2是c++编译,如果没有extern C,java层无法找到对应的native method。

剩余的工作就是在java层定义一个class, load libxmp.so,然后调用setImageGPano方法。如果app不crash,已经能说明成功一大半了。剩下的部分是需要将图片上传到facebook,google photo和line上,看看图片是否已经可以被识别为360全景图片了。

前面的脚本仅是build armv7-a架构下的库,如果需要生成其它的,修改参数即可。

源码

说了半天了,都是文字和图片。源码呢?
上面所有的代码(包括exiv2、expat以及修改的actions.cpp, configure, config.ac),脚本都已经放到github上了,大家可以下载。脚本是针对expat和exiv2分别写的,大家可根据需要自行合到一处。

https://github.com/hanzengbo/exiv2ForAndroid


Exiv2 With XMP for Android相关推荐

  1. Android开发 几个常用工具类

    本文出自[张鸿洋的博客]并 做了部分修改. 转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38965311 打开大家手上的项目,基 ...

  2. Android Adobe XMP与JPEG

    这篇文章的目的是简略的描述怎么在Android上将XMP信息写入JPEG文件,比如将图像的深度信息,全景图信息,连拍信息写入JPEG,Google Photos可以对一些特殊类型进行识别 Adobe ...

  3. Android 将MAP格式数据写入XML 将XMP文件读MAP数据格式中

    其中涉及的部分类可以自行查询,如:AtomicFile.java   FastXmlSerializer.java private static final String APPCONFIG_FILE ...

  4. 图形文件元数据管理工具exiv2

    图形文件元数据管理工具exiv2 图形文件通常都包含多种元数据,如Exif.IPTC.XMP.这些信息往往是渗透人员收集的目标.为了便于管理这些信息,Kali Linux内置了专用工具exiv2.该工 ...

  5. 安卓突击:Android 动画有哪几种?

    Tween Animation:通过对场景里的对象不断做图像变换(平移.缩放.旋转)产生动画效果,即是一种渐变动画: Frame Animation:顺序播放事先做好的图像,是一种画面转换动画. 一 ...

  6. android模拟器后台截屏,【Android】Android模拟器下截屏及格式转换

    一,模拟器信息 Android 模拟器手机的色深是16bit, 即R/G/B=5/6/5. 故需要一些特殊处理才能获得其屏幕图像. 我们可以adb登录进入Android模拟器,先dump出/dev/f ...

  7. (转)Android权限列表permission说明 (一)

    网络上不乏android权限列表,但是很少有将列表和使用方法放在一起的,所以特此总结一下 需要在AndroidManifest.xml中定义相应的权限(以获取internet访问权限为例),如下: X ...

  8. Android三种姿势带你玩转360度全景图功能

    简介 大家好我是张鹏辉(道长)人如其名,我是天桥上算命的,转发这条博文,接下来一个月会有意想不到的惊喜发生.最近微博上的全景图火了,所以决定实现一下.工程里面图片资源来自网络,如有侵权请联系我,马上删 ...

  9. Android Q Beta 正式发布 | 精于形,安于内

    移动行业在 2019 年创新不断,随着 5G 时代的到来与折叠屏技术的成熟,智能设备正在迈向未来新时代,而 Android 更是处在颠覆创新的风口浪尖.通过与生态圈伙伴们的深度合作,我们从软件到硬件不 ...

最新文章

  1. 磁铁对于小型直流电机的影响
  2. 如何用javasript对Gridview的项目进行汇总统计?
  3. linux automake-1.16 编译错误 Try `--no-discard-stderr' if option outputs to stderr
  4. matlab如何输出D-H表,MATLAB结果怎么输出表格形式的数据?
  5. PHP gd库 验证码
  6. 【OS学习笔记】九 实模式:从汇编的角度理解栈结构
  7. 计算机辅助齿轮图标注,机械CAD齿轮画法
  8. 大数据学习笔记:初探大数据世界
  9. python微信自动回复
  10. GitHub 年度报告,2017 年最受欢迎的编程语言
  11. C语言计算器详细教程(四则运算、小数、括号)
  12. VMware虚拟机磁盘扩容
  13. 第 42 章 RTC—实时时钟
  14. jsp简介lamitry_[提拉米苏] 找人一起玩,今晚刚开的号
  15. 2015,走上人生巅峰,让APK编辑器祝您一臂之力!
  16. 又踩坑之16G的大avi文件的删除
  17. Codeforces 950C-Zebras(模拟构造)
  18. 图片base64编码的前端展示及后端解码,编码
  19. Sed 流文本编辑器
  20. 硕飞编程器 量产烧录 Flash芯片(W25Q16DV)Bin文件流程说明

热门文章

  1. 如何做一个靠谱的产品经理
  2. MATLAB(2)--MATLAB矩阵的表示
  3. 磁盘无损简单卷转主分区
  4. 如何在PDF文档中添加手写签名?
  5. Electron 主进程和渲染进程互相通信
  6. Python 通过URL打开图片的几种方式
  7. 这可能是JAVA程序员进阶架构师的最佳之路了 !
  8. Qt|QToolBtton实现三态图标及文字变化
  9. 软件架构基本技法——打包
  10. 《Android开发源码精编解析》最新PDF版开源,安卓工程师进阶实战