本文会介绍在Android项目中使用OpenCV的一种方法,并会给出两个demo。
文中涉及开发环境在《Windows下Android开发环境的配置》的基础之上进行搭建。
在http://sourceforge.net/projects/opencvlibrary/files/opencv-android/2.4.8/下载OpenCV-2.4.8-android-sdk.zip。假定解压到目录D:\workspace\opencv-android-sdk。
打开Eclipse,点击File->Import->General->Existing Projects into Workspace,找到D:\workspace\opencv-android-sdk,选择导入OpenCV Library,也可以再导入一两个sample,但最好不要全部导入,因为如果设置了自动编译,会降低每次打开Eclipse的速度。
导入完成后,右击OpenCV Library,点击Properties->Android,选择合适的Build Project Target,即Android的API版本。
可以尝试运行一下OpenCV sample项目,检查一下设置是否正确。
仿照文章《Windows下Android开发环境的配置》的步骤创建一个新的Android项目。假定包名为net.johnhany.grayprocessjni。所需添加的文件及代码如下所示:
GrayProcess.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
package
net
.
johnhany
.
grayprocessjni
;
import
org
.
opencv
.
android
.
BaseLoaderCallback
;
import
org
.
opencv
.
android
.
LoaderCallbackInterface
;
import
org
.
opencv
.
android
.
OpenCVLoader
;
import
android
.
os
.
Bundle
;
import
android
.
app
.
Activity
;
import
android
.
graphics
.
Bitmap
;
import
android
.
graphics
.
BitmapFactory
;
import
android
.
graphics
.
Bitmap
.
Config
;
import
android
.
view
.
View
;
import
android
.
view
.
View
.
OnClickListener
;
import
android
.
widget
.
Button
;
import
android
.
widget
.
ImageView
;
public
class
GrayProcess
extends
Activity
implements
OnClickListener
{
private
Button
btnProc
;
private
ImageView
imageView
;
private
Bitmap
bmp
;
private
BaseLoaderCallback
mLoaderCallback
=
new
BaseLoaderCallback
(
this
)
{
@
Override
public
void
onManagerConnected
(
int
status
)
{
switch
(
status
)
{
case
LoaderCallbackInterface
.
SUCCESS
:
{
System
.
loadLibrary
(
"image_proc"
)
;
}
break
;
default
:
{
super
.
onManagerConnected
(
status
)
;
}
break
;
}
}
}
;
@
Override
public
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
)
;
setContentView
(
R
.
layout
.
activity_gray_process
)
;
btnProc
=
(
Button
)
findViewById
(
R
.
id
.
btn_gray_process
)
;
imageView
=
(
ImageView
)
findViewById
(
R
.
id
.
image_view
)
;
bmp
=
BitmapFactory
.
decodeResource
(
getResources
(
)
,
R
.
drawable
.
testpic1
)
;
imageView
.
setImageBitmap
(
bmp
)
;
btnProc
.
setOnClickListener
(
this
)
;
}
@
Override
public
void
onClick
(
View
v
)
{
int
w
=
bmp
.
getWidth
(
)
;
int
h
=
bmp
.
getHeight
(
)
;
int
[
]
pixels
=
new
int
[
w*
h
]
;
bmp
.
getPixels
(
pixels
,
0
,
w
,
0
,
0
,
w
,
h
)
;
int
[
]
resultInt
=
ImageProc
.
grayProc
(
pixels
,
w
,
h
)
;
Bitmap
resultImg
=
Bitmap
.
createBitmap
(
w
,
h
,
Config
.
ARGB_8888
)
;
resultImg
.
setPixels
(
resultInt
,
0
,
w
,
0
,
0
,
w
,
h
)
;
imageView
.
setImageBitmap
(
resultImg
)
;
}
@
Override
public
void
onResume
(
)
{
super
.
onResume
(
)
;
OpenCVLoader
.
initAsync
(
OpenCVLoader
.
OPENCV_VERSION_2_4_8
,
this
,
mLoaderCallback
)
;
}
}
|
ImageProcess.java
1
2
3
4
5
|
package
net
.
johnhany
.
grayprocessjni
;
public
class
ImageProc
{
public
static
native
int
[
]
grayProc
(
int
[
]
pixels
,
int
w
,
int
h
)
;
}
|
activity_gray_process.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<
LinearLayout
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
xmlns
:
tools
=
"http://schemas.android.com/tools"
xmlns
:
opencv
=
"http://schemas.android.com/apk/res-auto"
android
:
orientation
=
"vertical"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"match_parent"
>
<
Button
android
:
id
=
"@+id/btn_gray_process"
android
:
layout_width
=
"fill_parent"
android
:
layout_height
=
"wrap_content"
android
:
text
=
"@string/str_proc"
/
>
<
ImageView
android
:
id
=
"@+id/image_view"
android
:
layout_width
=
"wrap_content"
android
:
layout_height
=
"wrap_content"
android
:
contentDescription
=
"@string/str_proc"
/
>
<
/
LinearLayout
>
|
res\values\strings.xml
1
2
3
4
5
6
7
8
9
|
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
resources
>
<
string
name
=
"app_name"
>
GrayProcessJNI
<
/
string
>
<
string
name
=
"action_settings"
>
Settings
<
/
string
>
<
string
name
=
"str_proc"
>
gray
process
<
/
string
>
<
string
name
=
"str_desc"
>
image
description
<
/
string
>
<
/
resources
>
|
把一张图片拷贝到tes\drawable-hdpi,假设图片名称为testpic1。
在项目目录里新建一个名为“jni”的文件夹,里面添加如下3个文件:
Android.mk
1
2
3
4
5
6
7
|
LOCAL_PATH
:
=
$
(
call
my
-
dir
)
include
$
(
CLEAR_VARS
)
include
D
:
\
workspace
\
opencv
-
android
-
sdk
\
sdk
\
native
\
jni
\
OpenCV
.
mk
LOCAL_SRC_FILES
:
=
ImageProc
.
cpp
LOCAL_MODULE
:
=
image_proc
include
$
(
BUILD_SHARED_LIBRARY
)
|
这个文件负责把cpp或c文件编译成可以被Android程序调用的.so库.
Application.mk
1
2
3
4
|
APP_STL
:
=
gnustl_static
APP_CPPFLAGS
:
=
-
frtti
-
fexceptions
APP_ABI
:
=
armeabi
-
v7a
APP_PLATFORM
:
=
android
-
15
|
ImageProc.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#include <ImageProc.h>
#include <opencv2/core/core.hpp>
#include <string>
#include <vector>
using
namespace
cv
;
using
namespace
std
;
JNIEXPORT
jintArray
JNICALL
Java_net_johnhany_grayprocessjni_ImageProc_grayProc
(
JNIEnv*
env
,
jclass
obj
,
jintArray
buf
,
jint
w
,
jint
h
)
{
jint *
cbuf
;
cbuf
=
env
->
GetIntArrayElements
(
buf
,
false
)
;
if
(
cbuf
==
NULL
)
{
return
0
;
}
Mat
imgData
(
h
,
w
,
CV_8UC4
,
(
unsigned
char
*
)
cbuf
)
;
uchar*
ptr
=
imgData
.
ptr
(
0
)
;
for
(
int
i
=
0
;
i
<
w*
h
;
i
++
)
{
int
grayScale
=
(
int
)
(
ptr
[
4
*
i
+
2
]
*
0.299
+
ptr
[
4
*
i
+
1
]
*
0.587
+
ptr
[
4
*
i
+
0
]
*
0.114
)
;
ptr
[
4
*
i
+
1
]
=
grayScale
;
ptr
[
4
*
i
+
2
]
=
grayScale
;
ptr
[
4
*
i
+
0
]
=
grayScale
;
}
int
size
=
w *
h
;
jintArray
result
=
env
->
NewIntArray
(
size
)
;
env
->
SetIntArrayRegion
(
result
,
0
,
size
,
cbuf
)
;
env
->
ReleaseIntArrayElements
(
buf
,
cbuf
,
0
)
;
return
result
;
}
|
这个cpp文件负责调用OpenCV函数进行图像处理。
项目结构大致如图:
打开cmd.exe,输入:
1
2
3
|
cd
D
:
\
workspace
\
GrayProcessJni
\
bin
\
classes
D
:
javah
net
.
johnhany
.
grayprocessjni
.
ImageProc
|
此时classes文件夹内会多出一个net_johnhany_grayprocessjni_ImageProc.h文件,把它拷贝到jni文件夹内,并把名字改为ImageProc.h。
在Eclipse内点击Window->Preferences->C/C++->Build->Environment,增加一个环境变量:
变量名 | 值 |
NDKROOT | D:\android-ndk |
右击项目名称,点击New->Other->C/C++->Convert to a C/C++ Project (Adds C/C++ Nature)。
选择C++ Project,工具链选择Makefile的Other Toolchain(由于我的项目已经转换过,所以没有显示在项目名称列表里)。
转换完成后会提示是否切换到C/C++视图,选择不切换。
此时在代码文件内会有大量的错误,这是由于还没有包含进所需的C++库文件。
右击项目名称,点击Refresh。
再右击项目名称,点击Properties->Android,选择合适的Android API版本,点击Library中的Add,选择OpenCV Library。
点击C/C++ Build,把Builder Settings中的Build Command改为:
${NDKROOT}/ndk-build.cmd
Behaviour的设置如图:
点击C/C++ General->Paths and Symbols->Includes,在GNU C++中添加如下路径:
${NDKROOT}/platforms/android-15/arch-arm/usr/include
${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/include
${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include
${ProjDirPath}/../OpenCV-2.4.8-android-sdk/sdk/native/jni/include
这里的路径最好用NDKROOT或ProjDirPath的相对路径表示,如果用带有盘符的绝对路径可能会由于编译工具和Windows系统的表示方式不同而造成无法编译(如Cygwin)。
还要在安卓设备上安装OpenCV Manager。如果使用的是模拟器,打开cmd.exe,输入:
1
2
|
cd
D
:
\
android
-
sdk
\
platform
-
tools
adb
install
D
:
\
workspace
\
opencv
-
android
-
sdk
\
apk
\
OpenCV_2
.
4.8_Manager_2.16_armv7a
-
neon
.
apk
|
如果使用的是手机,把D:\workspace\opencv-android-sdk\apk目录下的OpenCV_2.4.8_Manager_2.16_armv7a-neon.apk拷贝到手机中,在手机中手动安装。
点击Run,会自动完成编译、打开模拟器、安装、启动运行等步骤;或者在编译之后把bin文件夹内的apk文件拷贝到手机里,手动安装,运行。
运行效果如下,点击按钮,图片变成灰色:
这里有另一个使用OpenCV的例子,支持的图像处理方法更多,可以切换图像,还可以用手指选择要作处理的区域:
https://github.com/johnhany/AndroidProj/tree/master/ImageProcess