如何在Android中使用OpenCV
2个回答
2015-01-12 · 知道合伙人数码行家
huanglenzhi
知道合伙人数码行家
向TA提问 私信TA
知道合伙人数码行家
采纳数:117538
获赞数:517195
长期从事计算机组装,维护,网络组建及管理。对计算机硬件、操作系统安装、典型网络设备具有详细认知。
向TA提问 私信TA
关注
展开全部
转载 作者RaySaint
如何在Android程序中使用OpenCV
有两种方式(重点讲后面一种):
1.使用OpenCV Java API。
OpenCV安装路径"F:\OpenCV-2.3.1-android-bin"下有两个文件夹,如下图
将文件夹"OpenCV-2.3.1"拷贝到你的Eclipse工作空间所在的目录,也就是在你的项目的上一级目录中,然后导入到工作空间中,在Package
Explorer中选择你的项目,单机右键在弹出菜单中选择Properties,然后在弹出的Properties窗口中左侧选择Android,然后点击右下方的Add按钮,选择OpenCV-2.3.1并点击OK,如下图:
此时,展开你的项目树,你可以看到新加了一个OpenCV-2.3.1_src目录,那么就是正确添加了OpenCV Java
API,否则就是你放置OpenCV-2.3.1的目录路径不正确。
然后就可以在你的Java源文件中导入OpenCV的API包,并且使用OpenCV API了,OpenCV API的包的形式如下:
Org.opencv.(OpenCV模块名).(OpenCV类名)
例如:
Org.opencv.core.Mat
2.利用JNI编写C++ OpenCV代码,通过Android NDK创建动态库(.so)
新建一个工作空间,例如"TestOpenCV",在Window->Preferences中设置好Android SDK的路径,如下图所示。
然后新建一个Android项目,Build
Target选择Android2.2,命名为"HaveImgFun",活动名改为HaveImgFun,Package
name中填写com.testopencv.haveimgfun,最后点击finish。
如同使用OpenCV Java
API那样,将OpenCV-2.3.1文件夹拷贝到与工作空间同一级目录中;另外,将"F:\OpenCV-2.3.1-android-bin\samples"下的includeOpenCV.mk文件拷贝到和项目HaveImgFun同一级目录中,如下图所示:
(上面这个各个文件夹和文件的放置很重要,因为OpenCV-2.3.1下的OpenCV.mk中有很多相对路径的指定,如果不是这样放置,在NDK生成动态库时可能会报文件或文件夹无法找到的错误)
选择Package Explorer中你的项目,右键选择new->folder,新建一个名为jni的文件夹,用来存放你的c/c++代码。
然后把res->layout下的main.xml的内容改为下面所示:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http。//schemas。android。com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <Button android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="@+id/btnNDK" android:text="使用C++ OpenCV进行处理" /> <Button android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="@+id/btnRestore" android:text="还原" /> <ImageView android:id="@+id/ImageView01" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </LinearLayout>
上面的代码就是一个线性布局里面包含2个按钮加上一个显示图像的ImageView
在文件夹src下的com.testopencv.haveimgfun包中新建一个类用于包装使用了opencv
c++代码的动态库的导出函数,类名为LibImgFun。
Eclipse会为你创建一个新的文件LibImgFun.java,将里面的内容改为:
package com.testopencv.haveimgfun; public class LibImgFun { static { System.loadLibrary("ImgFun"); } /** * @param width the current view width * @param height the current view height */ public static native int[] ImgFun(int[] buf, int w, int h); }
从上面的代码可以得知,我们的动态库名字应该为“libImgFun.so”,注意"public static native int[]
ImgFun(int[] buf, int w, int h)"中的native关键字,表明这个函数来自native
code。static表示这是一个静态函数,这样就可以直接用类名去调用。
在jni文件夹下建立一个"ImgFun.cpp"的文件,内容改为下面所示:
#include <jni.h> #include <stdio.h> #include <stdlib.h> #include <opencv2/opencv.hpp> using namespace cv; extern "C" { JNIEXPORT jintArray JNICALL Java_com_testopencv_haveimgfun_LibImgFun_ImgFun( JNIEnv* env, jobject obj, jintArray buf, int w, int h); JNIEXPORT jintArray JNICALL Java_com_testopencv_haveimgfun_LibImgFun_ImgFun( JNIEnv* env, jobject obj, jintArray buf, int w, int h){ jint *cbuf; cbuf = env->GetIntArrayElements(buf, false); if(cbuf == NULL) { return 0; } Mat myimg(h, w, CV_8UC4, (unsigned char*)cbuf); for(int j=0;j<myimg.rows/2;j++) { myimg.row(j).setTo(Scalar(0,0,0,0)); } int size=w * h; jintArray result = env->NewIntArray(size); env->SetIntArrayRegion(result, 0, size, cbuf); env->ReleaseIntArrayElements(buf, cbuf, 0); return result; } }
上面的代码中#include <jni.h>是必须要包含的头文件,#include
<opencv2/opencv.hpp>是opencv要包含的头文件。
动态库要导出的函数如下声明:
JNIEXPORT jintArray JNICALL
Java_com_testopencv_haveimgfun_LibImgFun_ImgFun(
JNIEnv* env, jobject obj, jintArray buf, int w, int h);
JNIEXPORT 和JNICALL是必须要加的关键字
jintArray就是int[],这里返回类型要么为空,要么为jni中定义的类型,事实上就是C\C++类型前面加上j,如果是数组,则在后面加上Array。
函数名的命名规则如下:
Java_(包路径)_(类名)_(函数名) (JNIEnv *env, jobject obj, 自己定义的参数...)
包路径中的"."用"_"(下划线)代替,类名就是上面包装该动态库函数的类的名字,最后一个才是真正的函数名;JNIEnv *env和jobject
obj这两个参数时必须的,用来调用JNI环境下的一些函数;后面就是你自己定义的参数。在这里,jintArray buf代表了传进来的图像的数据,int
w是图像的宽,int h是图像的高。
这个函数的功能是将传进来的图像的上半部分涂成黑色。
然后再在jni下新建两个文件"Android.mk"文件和"Application.mk"文件,这两个文件事实上就是简单的Makefile文件。
其中将Android.mk的内容改为如下所示:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) include ../includeOpenCV.mk ifeq ("$(wildcard $(OPENCV_MK_PATH))","") #try to load OpenCV.mk from default install location include $(TOOLCHAIN_PREBUILT_ROOT)/user/share/OpenCV/OpenCV.mk else include $(OPENCV_MK_PATH) endif LOCAL_MODULE := ImgFun LOCAL_SRC_FILES := ImgFun.cpp include $(BUILD_SHARED_LIBRARY)
Application.mk的内容改为如下所示:
APP_STL:=gnustl_static APP_CPPFLAGS:=-frtti -fexceptions APP_ABI:=armeabi armeabi-v7a
其中APP_ABI指定的是目标平台的CPU架构。(经过很多测试,android2.2必须指定为armeabi,android2.2以上的使用armeabi-v7a,如果没有设置对,很有可能安装到android虚拟机失败,当然你同时如上面写上也是可以的)
上面的步骤完成后,就可以使用NDK生成动态库了,打开cygwin,cd到项目目录下,如下图所示:
输入$NDK/ndk-build命令,开始创建动态库。成功的话如下图所示。
这时候刷新Eclipse的Package Explorer会出现两个新的文件夹obj和libs。
现在,只剩最后一步完成这个测试程序。
将一张图片,例如"lena.jpg"放到项目res->drawable-hdpi目录中并刷新该目录。
然后将HaveImgFun.java的内容改为下面所示:
package com.testopencv.haveimgfun;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.widget.Button;
import android.view.View;
import android.widget.ImageView;
public class HaveImgFun extends Activity {
/** Called when the activity is first created. */
ImageView imgView;
Button btnNDK, btnRestore;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.setTitle("使用NDK转换灰度图");
btnRestore=(Button)this.findViewById(R.id.btnRestore);
btnRestore.setOnClickListener(new ClickEvent());
btnNDK=(Button)this.findViewById(R.id.btnNDK);
btnNDK.setOnClickListener(new ClickEvent());
imgView=(ImageView)this.findViewById(R.id.ImageView01);
Bitmap img=((BitmapDrawable) getResources().getDrawable(R.drawable.lena)).getBitmap();
imgView.setImageBitmap(img);
}
class ClickEvent implements View.OnClickListener{
public void onClick(View v){
if(v == btnNDK)
{
long current=System.currentTimeMillis();
Bitmap img1=((BitmapDrawable) getResources().getDrawable(R.drawable.lena)).getBitmap();
int w=img1.getWidth(),h=img1.getHeight();
int[] pix = new int[w * h];
img1.getPixels(pix, 0, w, 0, 0, w, h);
int[] resultInt=LibImgFun.ImgFun(pix, w, h);
Bitmap resultImg=Bitmap.createBitmap(w, h, Config.RGB_565);
resultImg.setPixels(resultInt, 0, w, 0, 0,w, h);
long performance=System.currentTimeMillis()-current;
imgView.setImageBitmap(resultImg);
HaveImgFun.this.setTitle("w:"+String.valueOf(img1.getWidth())+",h:"+String.valueOf(img1.getHeight())
+" NDK耗时 "+String.valueOf(performance)+" 毫秒");
}
else if(v == btnRestore)
{
Bitmap img2=((BitmapDrawable) getResources().getDrawable(R.drawable.lena)).getBitmap();
imgView.setImageBitmap(img2);
HaveImgFun.this.setTitle("使用OpenCV进行图像处理");
}
}
}
}
点击全部保存,OK,现在可以选择一个Android虚拟机运行看一下效果,配置好Run Configuration然后点击Run
如何在Android程序中使用OpenCV
有两种方式(重点讲后面一种):
1.使用OpenCV Java API。
OpenCV安装路径"F:\OpenCV-2.3.1-android-bin"下有两个文件夹,如下图
将文件夹"OpenCV-2.3.1"拷贝到你的Eclipse工作空间所在的目录,也就是在你的项目的上一级目录中,然后导入到工作空间中,在Package
Explorer中选择你的项目,单机右键在弹出菜单中选择Properties,然后在弹出的Properties窗口中左侧选择Android,然后点击右下方的Add按钮,选择OpenCV-2.3.1并点击OK,如下图:
此时,展开你的项目树,你可以看到新加了一个OpenCV-2.3.1_src目录,那么就是正确添加了OpenCV Java
API,否则就是你放置OpenCV-2.3.1的目录路径不正确。
然后就可以在你的Java源文件中导入OpenCV的API包,并且使用OpenCV API了,OpenCV API的包的形式如下:
Org.opencv.(OpenCV模块名).(OpenCV类名)
例如:
Org.opencv.core.Mat
2.利用JNI编写C++ OpenCV代码,通过Android NDK创建动态库(.so)
新建一个工作空间,例如"TestOpenCV",在Window->Preferences中设置好Android SDK的路径,如下图所示。
然后新建一个Android项目,Build
Target选择Android2.2,命名为"HaveImgFun",活动名改为HaveImgFun,Package
name中填写com.testopencv.haveimgfun,最后点击finish。
如同使用OpenCV Java
API那样,将OpenCV-2.3.1文件夹拷贝到与工作空间同一级目录中;另外,将"F:\OpenCV-2.3.1-android-bin\samples"下的includeOpenCV.mk文件拷贝到和项目HaveImgFun同一级目录中,如下图所示:
(上面这个各个文件夹和文件的放置很重要,因为OpenCV-2.3.1下的OpenCV.mk中有很多相对路径的指定,如果不是这样放置,在NDK生成动态库时可能会报文件或文件夹无法找到的错误)
选择Package Explorer中你的项目,右键选择new->folder,新建一个名为jni的文件夹,用来存放你的c/c++代码。
然后把res->layout下的main.xml的内容改为下面所示:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http。//schemas。android。com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <Button android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="@+id/btnNDK" android:text="使用C++ OpenCV进行处理" /> <Button android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="@+id/btnRestore" android:text="还原" /> <ImageView android:id="@+id/ImageView01" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </LinearLayout>
上面的代码就是一个线性布局里面包含2个按钮加上一个显示图像的ImageView
在文件夹src下的com.testopencv.haveimgfun包中新建一个类用于包装使用了opencv
c++代码的动态库的导出函数,类名为LibImgFun。
Eclipse会为你创建一个新的文件LibImgFun.java,将里面的内容改为:
package com.testopencv.haveimgfun; public class LibImgFun { static { System.loadLibrary("ImgFun"); } /** * @param width the current view width * @param height the current view height */ public static native int[] ImgFun(int[] buf, int w, int h); }
从上面的代码可以得知,我们的动态库名字应该为“libImgFun.so”,注意"public static native int[]
ImgFun(int[] buf, int w, int h)"中的native关键字,表明这个函数来自native
code。static表示这是一个静态函数,这样就可以直接用类名去调用。
在jni文件夹下建立一个"ImgFun.cpp"的文件,内容改为下面所示:
#include <jni.h> #include <stdio.h> #include <stdlib.h> #include <opencv2/opencv.hpp> using namespace cv; extern "C" { JNIEXPORT jintArray JNICALL Java_com_testopencv_haveimgfun_LibImgFun_ImgFun( JNIEnv* env, jobject obj, jintArray buf, int w, int h); JNIEXPORT jintArray JNICALL Java_com_testopencv_haveimgfun_LibImgFun_ImgFun( JNIEnv* env, jobject obj, jintArray buf, int w, int h){ jint *cbuf; cbuf = env->GetIntArrayElements(buf, false); if(cbuf == NULL) { return 0; } Mat myimg(h, w, CV_8UC4, (unsigned char*)cbuf); for(int j=0;j<myimg.rows/2;j++) { myimg.row(j).setTo(Scalar(0,0,0,0)); } int size=w * h; jintArray result = env->NewIntArray(size); env->SetIntArrayRegion(result, 0, size, cbuf); env->ReleaseIntArrayElements(buf, cbuf, 0); return result; } }
上面的代码中#include <jni.h>是必须要包含的头文件,#include
<opencv2/opencv.hpp>是opencv要包含的头文件。
动态库要导出的函数如下声明:
JNIEXPORT jintArray JNICALL
Java_com_testopencv_haveimgfun_LibImgFun_ImgFun(
JNIEnv* env, jobject obj, jintArray buf, int w, int h);
JNIEXPORT 和JNICALL是必须要加的关键字
jintArray就是int[],这里返回类型要么为空,要么为jni中定义的类型,事实上就是C\C++类型前面加上j,如果是数组,则在后面加上Array。
函数名的命名规则如下:
Java_(包路径)_(类名)_(函数名) (JNIEnv *env, jobject obj, 自己定义的参数...)
包路径中的"."用"_"(下划线)代替,类名就是上面包装该动态库函数的类的名字,最后一个才是真正的函数名;JNIEnv *env和jobject
obj这两个参数时必须的,用来调用JNI环境下的一些函数;后面就是你自己定义的参数。在这里,jintArray buf代表了传进来的图像的数据,int
w是图像的宽,int h是图像的高。
这个函数的功能是将传进来的图像的上半部分涂成黑色。
然后再在jni下新建两个文件"Android.mk"文件和"Application.mk"文件,这两个文件事实上就是简单的Makefile文件。
其中将Android.mk的内容改为如下所示:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) include ../includeOpenCV.mk ifeq ("$(wildcard $(OPENCV_MK_PATH))","") #try to load OpenCV.mk from default install location include $(TOOLCHAIN_PREBUILT_ROOT)/user/share/OpenCV/OpenCV.mk else include $(OPENCV_MK_PATH) endif LOCAL_MODULE := ImgFun LOCAL_SRC_FILES := ImgFun.cpp include $(BUILD_SHARED_LIBRARY)
Application.mk的内容改为如下所示:
APP_STL:=gnustl_static APP_CPPFLAGS:=-frtti -fexceptions APP_ABI:=armeabi armeabi-v7a
其中APP_ABI指定的是目标平台的CPU架构。(经过很多测试,android2.2必须指定为armeabi,android2.2以上的使用armeabi-v7a,如果没有设置对,很有可能安装到android虚拟机失败,当然你同时如上面写上也是可以的)
上面的步骤完成后,就可以使用NDK生成动态库了,打开cygwin,cd到项目目录下,如下图所示:
输入$NDK/ndk-build命令,开始创建动态库。成功的话如下图所示。
这时候刷新Eclipse的Package Explorer会出现两个新的文件夹obj和libs。
现在,只剩最后一步完成这个测试程序。
将一张图片,例如"lena.jpg"放到项目res->drawable-hdpi目录中并刷新该目录。
然后将HaveImgFun.java的内容改为下面所示:
package com.testopencv.haveimgfun;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.widget.Button;
import android.view.View;
import android.widget.ImageView;
public class HaveImgFun extends Activity {
/** Called when the activity is first created. */
ImageView imgView;
Button btnNDK, btnRestore;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.setTitle("使用NDK转换灰度图");
btnRestore=(Button)this.findViewById(R.id.btnRestore);
btnRestore.setOnClickListener(new ClickEvent());
btnNDK=(Button)this.findViewById(R.id.btnNDK);
btnNDK.setOnClickListener(new ClickEvent());
imgView=(ImageView)this.findViewById(R.id.ImageView01);
Bitmap img=((BitmapDrawable) getResources().getDrawable(R.drawable.lena)).getBitmap();
imgView.setImageBitmap(img);
}
class ClickEvent implements View.OnClickListener{
public void onClick(View v){
if(v == btnNDK)
{
long current=System.currentTimeMillis();
Bitmap img1=((BitmapDrawable) getResources().getDrawable(R.drawable.lena)).getBitmap();
int w=img1.getWidth(),h=img1.getHeight();
int[] pix = new int[w * h];
img1.getPixels(pix, 0, w, 0, 0, w, h);
int[] resultInt=LibImgFun.ImgFun(pix, w, h);
Bitmap resultImg=Bitmap.createBitmap(w, h, Config.RGB_565);
resultImg.setPixels(resultInt, 0, w, 0, 0,w, h);
long performance=System.currentTimeMillis()-current;
imgView.setImageBitmap(resultImg);
HaveImgFun.this.setTitle("w:"+String.valueOf(img1.getWidth())+",h:"+String.valueOf(img1.getHeight())
+" NDK耗时 "+String.valueOf(performance)+" 毫秒");
}
else if(v == btnRestore)
{
Bitmap img2=((BitmapDrawable) getResources().getDrawable(R.drawable.lena)).getBitmap();
imgView.setImageBitmap(img2);
HaveImgFun.this.setTitle("使用OpenCV进行图像处理");
}
}
}
}
点击全部保存,OK,现在可以选择一个Android虚拟机运行看一下效果,配置好Run Configuration然后点击Run
展开全部
在Android中使用OpenCV方法为:
a、OpenCV安装路径"F:\OpenCV-2.3.1-android-bin"下有两个文件夹。将文件夹"OpenCV-2.3.1"拷贝到Eclipse工作空间所在的目录,也就是在你的项目的上一级目录中,然后导入到工作空间中,在Package Explorer中选择项目,单机右键在弹出菜单中选择Properties,然后在弹出的Properties窗口中左侧选择Android,然后点击右下方的Add按钮,选择OpenCV-2.3.1并点击OK,。
b、此时,展开项目树,可以看到新加了一个OpenCV-2.3.1_src目录,如下图,那么就是正确添加了OpenCV Java API,否则就是放置OpenCV-2.3.1的目录路径不正确。
c、然后就可以在Java源文件中导入OpenCV的API包,并且使用OpenCV API了,OpenCV API的包的形式如下:
Org.opencv.(OpenCV模块名).(OpenCV类名)
例如:
Org.opencv.core.Mat
a、OpenCV安装路径"F:\OpenCV-2.3.1-android-bin"下有两个文件夹。将文件夹"OpenCV-2.3.1"拷贝到Eclipse工作空间所在的目录,也就是在你的项目的上一级目录中,然后导入到工作空间中,在Package Explorer中选择项目,单机右键在弹出菜单中选择Properties,然后在弹出的Properties窗口中左侧选择Android,然后点击右下方的Add按钮,选择OpenCV-2.3.1并点击OK,。
b、此时,展开项目树,可以看到新加了一个OpenCV-2.3.1_src目录,如下图,那么就是正确添加了OpenCV Java API,否则就是放置OpenCV-2.3.1的目录路径不正确。
c、然后就可以在Java源文件中导入OpenCV的API包,并且使用OpenCV API了,OpenCV API的包的形式如下:
Org.opencv.(OpenCV模块名).(OpenCV类名)
例如:
Org.opencv.core.Mat
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询