android 图像识别 ndk,搭建NDK开发OpenCV环境

本文详细指导了如何在Android Studio中配置OpenCV环境,包括设置NDK,修改build.gradle和CMakeLists.txt,以及通过实例演示了如何使用OpenCV将图片转为灰度图。从官网介绍到实际操作,适合初学者入门。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大家好,今天我们来正式学习Android Studio环境下的OpenCV开发。

首先看看OpenCV官网,有这么一大段介绍(令人懵逼的开头):

425ce827b387

image

OpenCV于1999年由Intel建立,如今由Willow Garage提供支持。OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Mac OS、android、iOS等操作系统上。由一系列 C 函数和少量 C++ 类构成,所以它轻量级而且高效。还支持C#、Ch、ruby等语言进行编程,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。最新版本是3.4.1 ,2018年2月27日发布。

图像处理

利用计算机对图像进行分析处理,达到所需结果的技术,一般指的是数字图像处理,通过数码设备得到的数字图像是一个很大的二维数组,数组的元素叫像素,像素的值叫灰度值。主要的处理方法有去燥,增强,复原,分割,提取特征得到。

图像处理侧重于处理图像。

计算机视觉

研究如何使计算机可以像人一样“看”的一门科学,属于人工智能范畴,是用计算机来识别、追踪、测量等手机信息的科学。

计算机视觉侧重于模拟人的视觉。

应用领域

人机互动、物体识别、图像分割、人脸识别、动作识别、运动跟踪、机器人、运动识别、机器视觉、结构分析、汽车安全驾驶、自动驾驶等等。

OpenCV模块分析

core 核心功能模块。定义了被所有其他模块和基本数据结构(包括重要的多维数组Mat)使用的基本函数.包含核心功能,尤其是底层数据结构和算法函数。

基本的C语言数据结构和操作(Basic C Structures and Operations);

动态数据结构(Dynamic Structures);

数组操作相关函数(Operations on Arrays);

绘图功能(Drawing Functions);

XML和YAML语法的支持(XML/YAML Persistence);

XML和YAML语法的支持的C语言接口(XML/YAML Persistence (C API));

聚类(Clustering);

辅助功能与系统函数和宏(Utility and System Functions and Macros);

与OpenGL的互操作(OpenGL interoperability);

imgproc,是Image Processing的简写。图像处理模块。

线性和非线性的图像滤波(Image Filtering);

图像的几何变换(Geometric Image Transformations);

图像的其他变换(Miscellaneous Image Transformations);

直方图(Histograms);

结构分析和形状描述(Structural Analysis and Shape Descriptors);

运动分析和目标跟踪(Motion Analysis and Object Tracking);

特征检测(Feature Detection);

目标检测(Object Detection);

features2d,是2D Features Framework的简写。二维特征框架模块。

人脸识别

VR和AR

特征的检测和描述(Feature Detection and Description);

特征检测器的通用接口(Common Interfaces of Feature Detectors);

描述符提取器的通用接口(Common Interfaces of Descriptor Extractors);

描述符匹配器的通用接口(Common Interfaces of Descriptor Matchers);

通用描述符匹配器通用接口(Common Interfaces of Generic Descriptor Matchers);

关键点和匹配结果的绘制功能(Drawing Function of Keypoints and Matches);

目标分类(Object Categorization);

flann,Clustering and Search in Multi-Dimensional Spaces,多维空间聚类和搜索模块。

快速近视最近邻搜索(Fast Approximate Nearest Neighbor Search);

聚类(Clustering);

video,是Video Analysis的简写。视频分析模块。

运动分析和目标跟踪(Motion Analysis and Object Tracking),视频相关的,上面提到的是图片相关的。

calib3d,是Camera Calibration and 3D Reconstruction的简写。这个模块主要是相机校准和三维重建相关的内容,包括基本的多视角几何算法、单个立体摄像头标定、物体姿态估计、立体相似性算法,3D信息的重建等。

静态库

425ce827b387

image

OpenCV目录结构(3.3为例)

apk OpenCV manager针对各个架构的cpu的manager安装包,过去的OpenCV如果不用NDK方式开发,会要求手机上需要安装OpenCV manager的对应指令集的apk

samples android平台下的官方案例源码和安装包(Java调用方式)

SDK SDK文件夹,jni开发所需。

Mat

Mat是OpenCV的核心数据结构,从来表示任意N维矩阵。图像是二微矩阵的一种特殊场景,所以也可以使用Mat来表示。Mat是OpenCV中用到最多的类。定义在命名空间cv下。

Android Studio平台上搭建NDK环境(以MacOS环境为例)

1.Prefrences-->Android SDK-->SDK Tools

选择安装

cMake:外部构建工具。如果你准备只使用 ndk-build 的话,可以不使用它。

LLDB:Android Studio上面调试本地代码的工具

NDK

425ce827b387

image.png

Android Studio平台上搭建NDK开发OpenCV环境

1. file-->New-->New Project (必须选中 Include C++ support)

425ce827b387

image

2. 一路向下走到下面步骤,C++ Standard 需选中C++11标准。(14标准可能还不太稳定,也不能选择Toolchain Default标准,可能会出现莫名其妙的错误)

下方的两个复选框须勾选上。

425ce827b387

image

项目创建成功后,目录结构为:

425ce827b387

image

为了能够进行OpenCV开发,我们接下来需要对项目主工程下的build.gradle和CMakeLists.txt进行修改。

build.gradle新增内容

添加设备支持的二进制指令集

libs路径使用的是绝对路径,读取速度相对快一些,最好不使用相对路径,因为使用相对路径,需要将libs拷贝到项目中,而libs文件夹大小为四百多M

425ce827b387

image

CMakeLists.txt修改

该文件新增:

文件创建能力;

库文件的绝对路径;

头文件的具体路径;

动态库的引入(库名 libopencv_java3 动态SHARED 引入IMPORTEDS);

425ce827b387

image

文档尾部的target_link_libraries修改为:

425ce827b387

image

在环境搭建好之后,如何判断我们已经能够进行OpenCV开发呢?

其实很简单,在项目的cpp目录下的native-lib.cpp文件中,引入头文件core.hpp,如果能成功引入,则说明环境搭建已经完毕:include

至此,我们项目的OpenCV环境已经搭建完成。下面我们用一个小小的示例结束本文。

将图片变为灰度图

原图:

425ce827b387

image.png

灰度图:

425ce827b387

image.png

废话不多说,上代码

布局activity_main.xml

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:id="@+id/button1"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="点击处理" />

android:id="@+id/sample_text"

android:layout_width="wrap_content"

android:layout_height="wrap_content" />

页面MainActivity.java

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.os.Bundle;

import android.support.v7.app.AppCompatActivity;

import android.view.View;

import android.widget.Button;

import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {

private ImageView imageview;

private Button button;

private Bitmap bitmap;

static {

System.loadLibrary("native-lib");

}

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

imageview = findViewById(R.id.sample_text);

button = findViewById(R.id.button1);

bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.timg);

imageview.setImageBitmap(bitmap);

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

// 获取图片的宽

int w = bitmap.getWidth();

// 获取图片的高

int h = bitmap.getHeight();

// 建立数组pixels,长度为w * h

int[] pixels = new int[w * h];

// getPixels方法获取图片像素

bitmap.getPixels(pixels, 0, w, 0, 0, w, h);

// 调用jni方法获取灰度图片像素数组

int[] resultInt = grayP(pixels, w, h);

// 创建新的bitmap

Bitmap resultImg = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);

// 新的bitmap存入灰度图像素

resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);

imageview.setImageBitmap(resultImg);

}

});

}

/**

* 将图片灰度化并获取灰度化后的图片像素

* @param pixels int类型的数组-像素

* @param w 图片的宽

* @param h 图片的高

* @return

*/

public native int[] grayP(int[] pixels, int w, int h);

}

上述代码中,我们使用了getPixels方法(setPixels类似),那么此方法需要用到的参数都代表什么意思呢?

public void getPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height) {

throw new RuntimeException("Stub!");

}

public void setPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height) {

throw new RuntimeException("Stub!");

}

int[] pixels是存储图片的指定区域的所有像素的一维数组;

int offset是指定的偏移位置;

int x, int y, int width, int height是从指定的位置截取指定的宽高;

int stride 是指定在行之间跳过的像素的数目。图片是二维的,存入一个一维数组中,那么就需要这个参数来指定多少个像素换一行;

stride的特点:

在原图上截取的时候,需要读取stride个像素再去读原图的下一行

如果一行的像素个数足够,就读取stride个像素再下一行读

如果一行的像素个数不够,用0(透明)来填充到 stride个

得到的数据,依次存入pixels[]这个一维数组中

JNI native-lib.cpp

#include

#include

#include

#include

using namespace std;

using namespace cv;

extern "C"

JNIEXPORT jintArray JNICALL

Java_com_example_ndk_1opencv_MainActivity_grayP(

JNIEnv *env,

jclass type,

jintArray pixels_,

jint w,

jint h) {

jint *pixels = env->GetIntArrayElements(pixels_, NULL);

if (pixels == NULL) {

return 0;

}

//RGBA

Mat imgData(h, w, CV_8UC4, (unsigned char *) pixels);

uchar *ptr = imgData.ptr(0);

for (int i = 0; i < w * h; i++) {

// 灰度值计算公式 R * 0.3 + G * 0.59 + B * 0.11

uchar gray = (uchar) (ptr[4 * i + 2] * 0.299 + ptr[4 * i + 1] * 0.587 +

ptr[4 * i + 0] * 0.114);

ptr[4*i+0]=gray; // b

ptr[4*i+1]=gray;// g

ptr[4*i+2]=gray; // r

}

int size = w * h;

jintArray result = env->NewIntArray(size);

env->SetIntArrayRegion(result, 0, size, pixels);

env->ReleaseIntArrayElements(pixels_, pixels, 0);

return result;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值