最近产品须要作一个物体识别的app demo, 咨询研究人员以后,获得开源的yolo9000能够知足须要,yolo中使用的darknet是C语言编写的,yolo9000编译以后自己提供了命令行模式来生成识别结果,默认的结果是识别后带有画框的图片,如图:java
图片中框体title便是识别的结果(只有英文),首先想到的是能够经过java执行本地命令的方式来生成图片,而后将图片以接口的方式传给app,可是app拿到图片后就只能直接展现给用户,没法再作如翻译等进一步的处理。shell
基于以上状况,我想到的解决方案是:对darknet源码进行改写,添加识别物体返回json数据(包含物体名称,坐标,识别百分比等)的函数,再利用Java能够调用本地函数的特性直接调用该函数。json
JAVA调用C方法获取识别结果,思路是这样的:将用户传来的图片放到一个临时目录中,而后调用C函数分析,获得结果后,返回给用户。因此先定义一个调用C得本地方法app
package com.iflytek.research.yoloserver; /** * 对yolo9000的封装 * <p>调用本地库来识别图片中的物体</p> * @author ljgeng * */ public class Yolo { /** * 物体识别,函数会从指定的路径读取图片解析 * @param imgPath 图片的路径 * @return 识别的结果,json 格式的文本 */ public static native String predict(String imgPath); }
定义好函数以后,利用javah 工具自动生成c语言的头文件。函数
javah com.iflytek.research.yoloserver.Yolo
运行后会生成一个com_iflytek_research_yoloserver_Yolo.h 文件,将文件导入C项目中工具
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_iflytek_research_yoloserver_Yolo */ #ifndef _Included_com_iflytek_research_yoloserver_Yolo #define _Included_com_iflytek_research_yoloserver_Yolo #ifdef __cplusplus extern "C" { #endif /* * Class: com_iflytek_research_yoloserver_Yolo * Method: predict * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_iflytek_research_yoloserver_Yolo_predict (JNIEnv *, jclass, jstring); #ifdef __cplusplus } #endif #endif
在C项目中新建yolo.c, 引入刚刚的头文件,并实现predict方法学习
#include "com_iflytek_research_yoloserver_Yolo.h" #include "stdio.h" JNIEXPORT jstring JNICALL Java_com_iflytek_research_yoloserver_Yolo_predict (JNIEnv * env, jclass jcs, jstring jstr){ const char * str = (*env)->GetStringUTFChars(env,jstr,0); if (str == NULL) { return NULL; } printf("%s!\n",str); (*env)->ReleaseStringUTFChars(env, jstr, str); char * jsonStr = "{\"semantic\":{\"slots\":{\"name\":\"张三\"}}, \"rc\":0, \"operation\":\"CALL\", \"service\":\"telephone\", \"text\":\"打电话给张三\"}"; return (*env)->NewStringUTF(env, jsonStr); }
如今只是先跑通流程,因此在yolo.c中尚未真正调用object detection相关的方法,如下对predict函数的简单解释命令行
const char * str = (*env)->GetStringUTFChars(env,jstr,0); // 调用jni 函数GetStringUTFChars 读取Java String 对象内容 printf("%s!\n",str); // 打印 return (*env)->NewStringUTF(env, jsonStr); // 调用jni函数NewStringUTF 返回一个Java String对象。
JNI 有很多函数,有兴趣能够去官网或者相关博客学习一下。翻译
写好C代码以后,将其编译到动态库中,供Java调用,我使用的是window系统,因而安装了cygwin64,并带上gcc功能。code
x86_64-w64-mingw32-gcc.exe -D __int64="long long" -I "C:\Program Files\Java\jdk1.8.0_151\include" -I "C:\Program Files\Java\jdk1.8.0_151\include\win32" -shared -o yolo.dll yolo.c -W
具体使用哪一个gcc命令,看系统实际状况。成功后,生成的yolo.dll 拷贝到Java项目根目录,加载库后运行。
package com.iflytek.research.yoloserver; /** * 程序入口 * */ public class YoloServerApp { static { System.loadLibrary("yolo"); } public static void main(String[] args) { String re = Yolo.predict("你好"); System.out.println(re); } }
使用静态代码块先加载库,而后运行predict函数,成功返回了json字符串。