@w460461339
2017-06-09T19:30:25.000000Z
字数 13953
阅读 2311
研究生毕业设计
Android
按照 《研究生毕业设计7》中的方法进行本次项目的build时,总是过不了。应该是cmake文件写的有问题,接下来还是需要好好学些cmake文件怎么写才行呀。
这里可以看到,项目中并没有jni文件夹,这也是这个方法和之前方法最不同的一点。
方法和前面一样,这里再说一遍。
opencv用的版本是3.1.0
1、 File -> new -> import module 选择openc目录下sdk下的java文件夹
2、 导入后build会有错误,将左侧android视图切换到project视图,把openCVLibrary310文件夹下的build.gradle打开,修改里面的android平台以及buildtools等参数,使其符合自己的设置。直到不报错
3、 File -> project structure -> app -> dependencies 点击绿色加好,选择3 module 之类的,将openCVLibrary310导入加入到dependencies即可。
至此,opencv添加完成。(注意,这里不需要再导入cpu架构包了)
功能:
1. 通过摄像头拍照获取图像数据。
2. 将图像数据保存,并切换到处理Activity。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.wzf.face.landmarks2.MainActivity">
<Button
android:layout_below="@+id/javaCVLMD"
android:onClick="click1"
android:layout_width="60dp"
android:layout_height="40dp"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:text="按钮1"
/>
<org.opencv.android.JavaCameraView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/javaCVLMD"/>
</RelativeLayout>
public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2 {
public static final String TAG="LMD";
JavaCameraView javaCameraView;
Mat mRGBA;
/*
接受onResume里面的回调信息,
如果是success,表示opencv加载成功,那么使能显示控件。
否则执行默认操作
*/
BaseLoaderCallback baseLoaderCallback=new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status){
case BaseLoaderCallback.SUCCESS:{
javaCameraView.enableView();
break;
}
default:{
super.onManagerConnected(status);
break;
}
}
}
};
static{
System.loadLibrary("MyLibs");
}
/**
获取控件
**/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
javaCameraView=(JavaCameraView)findViewById(R.id.javaCVLMD);
javaCameraView.setVisibility(View.VISIBLE);
javaCameraView.setCvCameraViewListener(this);
}
/**
三个activity的生命周期方法,
用来判断什么时候应该开启opencv的相机,
什么时候应该将相机显示控件释放。
**/
@Override
protected void onPause() {
super.onPause();
if(javaCameraView!=null){
javaCameraView.disableView();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if(javaCameraView!=null){
javaCameraView.disableView();
}
}
@Override
protected void onResume() {
super.onResume();
if(OpenCVLoader.initDebug()){
Log.i(TAG,"成功");
baseLoaderCallback.onManagerConnected(BaseLoaderCallback.SUCCESS);
}else{
Log.i(TAG,"失败");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_1_0,this,baseLoaderCallback);
}
}
/**
opencv相机监听接口的三个方法的实现,负责将相机帧数据进行初始化,返回到控件以及释放
**/
@Override
public void onCameraViewStarted(int width, int height) {
mRGBA=new Mat(height,width, CvType.CV_8UC4);
}
@Override
public void onCameraViewStopped() {
mRGBA.release();
}
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
mRGBA=inputFrame.rgba();
return mRGBA;
}
/**
按钮点击事件,点击后,先保存数据,再跳转到另一个activity
**/
public void click1(View view){
saveImage(mRGBA);
Intent intent=new Intent(this,DetailActivity.class);
startActivity(intent);
}
/**
保存图片的方法
**/
public void saveImage(Mat subImg){
Bitmap bmp=null;
try{
bmp= Bitmap.createBitmap(subImg.cols(),subImg.rows(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(subImg,bmp);
}catch (Exception e){
Log.d(TAG,e.getMessage());
}
subImg.release();
FileOutputStream out=null;
String fileName="frame.png";
File sd=new File(Environment.getExternalStorageDirectory()+"/frames");
boolean success=true;
if(!sd.exists()){
success=sd.mkdir();
}
if(success){
File dest=new File(sd,fileName);
try{
out=new FileOutputStream(dest);
bmp.compress(Bitmap.CompressFormat.PNG,100,out);
}catch (Exception e){
Log.d(TAG,e.getMessage());
}finally {
try{
if(out!=null){
out.close();
Log.d(TAG,"成功赋值");
}
}catch (Exception e){
Log.d(TAG,e.getMessage());
}
}
}
}
}
创建完activity后,不要忘了在清单文件中进行注册。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/iv_detail"/>
<Button
android:text="来吧!"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn_detail" />
</LinearLayout>
public class DetailActivity extends AppCompatActivity {
ImageView imageView;
Button btnProceess;
Bitmap bitmapInput,bitmapOutput;
Mat matInput,matOutput;
// 加载本地方法库
static{
System.loadLibrary("MyLibs");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
// 获得控件
imageView=(ImageView)findViewById(R.id.iv_detail);
btnProceess=(Button)findViewById(R.id.btn_detail);
// 获得MainActivity中保存的图片的路径
String photoPath= Environment.getExternalStorageDirectory()+"/frames/frame.png";
// 设置Bitmap的显示信息
BitmapFactory.Options options=new BitmapFactory.Options();
options.inPreferredConfig=Bitmap.Config.ARGB_8888;
bitmapInput=BitmapFactory.decodeFile(photoPath,options);
// 展示原图
imageView.setImageBitmap(bitmapInput);
// 将Bitmap转化为Mat图片,并创建等大小的新的Bitmap
matInput=convertBitmap2Mat(bitmapInput);
matOutput=new Mat(matInput.rows(),matInput.cols(), CvType.CV_8UC3);
// 按钮事件监听器
btnProceess.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean boo= OpenCVLoader.initDebug();
if(boo){
Toast.makeText(DetailActivity.this,"Calling native function",Toast.LENGTH_SHORT).show();
// 调用本地方法,进行landmarks检测
NativeClass.LandmarkDetection(matInput.getNativeObjAddr(),matOutput.getNativeObjAddr());
Toast.makeText(DetailActivity.this,"Calling native function222",Toast.LENGTH_SHORT).show();
// 将Mat转换为Bitmap,并进行显示
bitmapOutput=convertMat2Bitmap(matOutput);
imageView.setImageBitmap(bitmapOutput);
}else{
Toast.makeText(DetailActivity.this,"失败",Toast.LENGTH_SHORT).show();
}
}
});
}
// Mat转换成Bitmap
Bitmap convertMat2Bitmap(Mat img){
int width=img.width();
int height=img.height();
Bitmap bmp;
bmp=Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
Mat tmp;
tmp=img.channels()==1?new Mat(width,height,CvType.CV_8UC1,new Scalar(1)):new Mat(width,height,CvType.CV_8UC3,new Scalar(3));
try{
if(img.channels()==3){
Imgproc.cvtColor(img,tmp,Imgproc.COLOR_RGB2BGRA);
}else if(img.channels()==1){
Imgproc.cvtColor(img,tmp,Imgproc.COLOR_GRAY2BGRA);
}
Utils.matToBitmap(tmp,bmp);
}catch(Exception e){
Log.d("Exception",e.getMessage());
}
return bmp;
}
//bitmap转换成Mat
Mat convertBitmap2Mat(Bitmap rgbaImg){
Mat rgbaMat=new Mat(rgbaImg.getHeight(),rgbaImg.getWidth(),CvType.CV_8UC4);
Bitmap bmp32=rgbaImg.copy(Bitmap.Config.ARGB_8888,true);
Utils.bitmapToMat(bmp32,rgbaMat);
Mat rgbNewMat=new Mat(rgbaImg.getHeight(),rgbaImg.getWidth(),CvType.CV_8UC3);
Imgproc.cvtColor(rgbaMat,rgbNewMat, Imgproc.COLOR_RGB2BGR,3);
return rgbNewMat;
}
}
package com.wzf.face.landmarks2;
public class NativeClass {
public native static void LandmarkDetection(long addrInput,long addrOutput);
}
从android studio的命令行中,输入
cd app/src/main
javah -d jni -classpath ../../build/intermediates/classes/debuge com.wzf.face.landmarks2.NativeClass (这个是nativeclass的copy reference结果,即包名+类名)
下一步很关键!
在完成上述两个命令后,如果没有出错的话,之后你会获得一个jni文件夹,里面有一个名字还算长的.h文件。将该jni文件夹复制,然后随便找一个地方进行放置。完成后删除项目中的jni文件夹。
比如我就把它放到了一个名为generateLibs的文件夹下。
进入jni文件夹,拷贝.h文件一份,将其重命名为.cpp文件。
/* DO NOT EDIT THIS FILE - it is machine generated */
// 以下是需要到的库文件
#include <iostream>
#include <jni.h>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <dlib/opencv.h>
#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/image_processing/render_face_detections.h>
#include <dlib/image_processing.h>
#include <dlib/gui_widgets.h>
/* Header for class com_wzf_face_landmarks2_NativeClass */
// 定义命名空间
using namespace cv;
using namespace dlib;
using namespace std;
#ifndef _Included_com_wzf_face_landmarks2_NativeClass
#define _Included_com_wzf_face_landmarks2_NativeClass
#ifdef __cplusplus
extern "C" {
#endif
// 声明两个方法
void faceDetectiononDlib(Mat& img,Mat& dst);
void renderToMat(std::vector<full_object_detection>& dets, Mat& dst);
/*
* Class: com_wzf_face_landmarks2_NativeClass
* Method: LandmarkDetection
* Signature: (JJ)V
*/
// 仔细看会发下,这个方法就对应于我们在Nativeclass.java中声明的方法
JNIEXPORT void JNICALL Java_com_wzf_face_landmarks2_NativeClass_LandmarkDetection
(JNIEnv *, jclass, jlong, jlong);
#ifdef __cplusplus
}
#endif
#endif
/* DO NOT EDIT THIS FILE - it is machine generated */
// 别忘了添加自己的库依赖(com_wzf_face_landmarks2_NativeClass.h)
#include <jni.h>
#include <com_wzf_face_landmarks2_NativeClass.h>
#include "com_wzf_face_landmarks2_NativeClass.h"
/* Header for class com_wzf_face_landmarksdetection_NativeClass */
/*
* Class: com_wzf_face_landmarksdetection_NativeClass
* Method: LandmarkDetection
* Signature: (JJ)V
*/
// 同理,对应于NativeClass.java中声明的方法
JNIEXPORT void JNICALL Java_com_wzf_face_landmarks2_NativeClass_LandmarkDetection
(JNIEnv *env, jclass thiz, jlong addrInput, jlong addrOutput){
// 获取传入的参数,进行强转
Mat& image=*(Mat*) addrInput;
Mat& dst=*(Mat*) addrOutput;
// 利用dlib进行检测
faceDetectiononDlib(image,dst);
}
void faceDetectiononDlib(Mat& img, Mat& dst){
try{
// 获取检测器
frontal_face_detector detector=get_frontal_face_detector();
shape_predictor pose_model;
// 加载参数
deserialize("storage/emulated/0/shape_predictor_68_face_landmarks.dat")>>pose_model;
// 将opencv图像转化为dlib图像
cv_image<bgr_pixel> cimg(img);
// 检测,面部信息存储在faces中
std::vector<dlib::rectangle> faces=detector(cimg);
std::vector<full_object_detection> shapes;
// 这步不是很懂,存储面部的landmarks信息?
for(unsigned long i=0;i<faces.size();i++){
shapes.push_back(pose_model(cimg,faces[i]));
}
// 将结果赋值到dst对象内
dst = img.clone();
// 将landmarks进行绘图,以便观看显示
renderToMat(shapes,dst);
}catch(serialization_error& e) {
cout<<endl<<e.what()<<endl;
}
}
// 对landmarks进行绘图显示。
void renderToMat(std::vector<full_object_detection>& dets, Mat& dst){
Scalar color;
int sz = 3;
color = Scalar(0,255,0);
//chin line
for(unsigned long idx = 0; idx < dets.size(); idx++){
for (unsigned long i = 1; i <= 16; ++i)
cv::line(dst, Point(dets[idx].part(i).x(), dets[idx].part(i).y()), Point(dets[idx].part(i - 1).x(), dets[idx].part(i - 1).y()), color, sz);
//line on top of nose
for (unsigned long i = 28; i <= 30; ++i)
cv::line(dst, Point(dets[idx].part(i).x(), dets[idx].part(i).y()), Point(dets[idx].part(i - 1).x(), dets[idx].part(i - 1).y()), color, sz);
//left eyebrow
for (unsigned long i = 18; i <= 21; ++i)
cv::line(dst, Point(dets[idx].part(i).x(), dets[idx].part(i).y()), Point(dets[idx].part(i - 1).x(), dets[idx].part(i - 1).y()), color, sz);
//right eyebrow
for (unsigned long i = 23; i <= 26; ++i)
cv::line(dst, Point(dets[idx].part(i).x(), dets[idx].part(i).y()), Point(dets[idx].part(i - 1).x(), dets[idx].part(i - 1).y()), color, sz);
//bottom of nose
for (unsigned long i = 31; i <= 35; ++i)
cv::line(dst, Point(dets[idx].part(i).x(), dets[idx].part(i).y()), Point(dets[idx].part(i - 1).x(), dets[idx].part(i - 1).y()), color, sz);
cv::line(dst, Point(dets[idx].part(30).x(),dets[idx].part(30).y()), Point(dets[idx].part(35).x(), dets[idx].part(35).y()), color, sz);
//left eye
for (unsigned long i = 37; i <= 41; ++i)
cv::line(dst, Point(dets[idx].part(i).x(), dets[idx].part(i).y()), Point(dets[idx].part(i - 1).x(), dets[idx].part(i - 1).y()), color, sz);
cv::line(dst, Point(dets[idx].part(36).x(),dets[idx].part(36).y()), Point(dets[idx].part(41).x(), dets[idx].part(41).y()), color, sz);
//right eye
for (unsigned long i = 43; i <= 47; ++i)
cv::line(dst, Point(dets[idx].part(i).x(), dets[idx].part(i).y()), Point(dets[idx].part(i - 1).x(), dets[idx].part(i - 1).y()), color, sz);
cv::line(dst, Point(dets[idx].part(42).x(),dets[idx].part(42).y()), Point(dets[idx].part(47).x(), dets[idx].part(47).y()), color, sz);
//lips out part
for (unsigned long i = 49; i <= 59; ++i)
cv::line(dst, Point(dets[idx].part(i).x(), dets[idx].part(i).y()), Point(dets[idx].part(i - 1).x(), dets[idx].part(i - 1).y()), color, sz);
cv::line(dst, Point(dets[idx].part(48).x(),dets[idx].part(48).y()), Point(dets[idx].part(59).x(), dets[idx].part(59).y()), color, sz);
//lips inside part
for (unsigned long i = 61; i <= 67; ++i)
cv::line(dst, Point(dets[idx].part(i).x(), dets[idx].part(i).y()), Point(dets[idx].part(i - 1).x(), dets[idx].part(i - 1).y()), color, sz);
cv::line(dst, Point(dets[idx].part(60).x(),dets[idx].part(60).y()), Point(dets[idx].part(67).x(), dets[idx].part(67).y()), color, sz);
}
}
在复制粘贴别人代码的时候,请千万注意,方法名是否和自己定义的java类中的方法名以及包名对的上。 我就是在这里卡了很久的= - 。
去dlib官网下载dlib,解压即可,我用的是19.1
解压后,将解压出来的文件夹放置在jni文件夹下,如果名字不是dlib的话,重命名为dlib(只是为了写mk文件方便)。
1、与jni文件夹同级时,创建文件夹,名为third_party.
2、在third_party内,创建文件夹,名为opencv
3、将“opencv安装路径”\sdk\native 下的三个文件夹拷贝至2中创建的文件夹内。
至此,导入完成。
LOCAL_PATH := $(call my-dir)
# 加载dlib库
include $(CLEAR_VARS)
LOCAL_MODULE :=dlib
LOCAL_C_INCLUDES :=$(LOCAL_PATH)/dlib
LOCAL_SRC_FILES += \
../$(LOCAL_PATH)/dlib/dlib/threads/threads_kernel_shared.cpp \
../$(LOCAL_PATH)/dlib/dlib/entropy_decoder/entropy_decoder_kernel_2.cpp \
../$(LOCAL_PATH)/dlib/dlib/base64/base64_kernel_1.cpp \
../$(LOCAL_PATH)/dlib/dlib/threads/threads_kernel_1.cpp \
../$(LOCAL_PATH)/dlib/dlib/threads/threads_kernel_2.cpp
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
include $(BUILD_STATIC_LIBRARY)
#加载opencv库
TOP_LEVEL_PATH :=$(abspath $(LOCAL_PATH)/..)
####$(info TOP Level Path: $(TOP_LEVEL_PATH))#####
EXT_INSTALL_PATH = $(TOP_LEVEL_PATH)/third_party
OPENCV_PATH = $(EXT_INSTALL_PATH)/opencv/jni
OPENCV_INCLUDE_DIR = $(OPENCV_PATH)/include
include $(CLEAR_VARS)
OPENCV_INSTALL_MODULES :=on
OPENCV_CAMERA_MODULES :=on
OPENCV_LIB_TYPE :=SHARED
include $(OPENCV_PATH)/OpenCV.mk
# 给自己的库取个名字
LOCAL_MODULE := MyLibs
LOCAL_C_INCLUDES += \ $(OPENCV_INCLUDE_DIR)
# 指定源文件,即之前编写的.cpp文件
LOCAL_SRC_FILES := com_wzf_face_landmarks2_NativeClass.cpp
LOCAL_LDLIBS += -lm -llog -ldl -lz -ljnigraphics
LOCAL_CPPFLAGS += -fexceptions -frtti -std=c++11
LOCAL_STATIC_LIBRARIES +=dlib
include $(BUILD_SHARED_LIBRARY)
NDK_TOOLCHAIN_VERSION := clang
#指定自己目标的cpu库
APP_ABI := arm64-v8a
APP_CPPFLAGS := -std=c++11 -frtti -fexceptions
# 指定目标android level
APP_PLATFORM := android-23
APP_STL := gnustl_static
1、 在jni和third_party所在的文件件内打开命令行窗口。
2、 确保你已经安装了ndk,并可以在任何地方运行(配置环境变量就好),教程自行google.
3、 在命令行窗口内输入ndk-build,若没有错误,等待即可。
4、 若有错误,也是自己编写的.cpp或者.h文件中的问题,根据提示修改即可。
5、完成后即可看见libs和obj两个文件夹。
1、 进入libs文件夹,里面有你目标cpu架构的
2、 在android studio中,右击app,创建JNI文件夹,并命名为jniLibs。
3、 将1中的cpu架构文件夹,粘贴到2中生成的jniLibs文件夹下,即可。