安卓NDK开发——常用数据std::string、jstring、cv::Mat、Bitmap互转
2023-12-15 15:27:00
前言
在 Android NDK开发中,JNI可以在 Java 和本地代码(如 C、C++)之间进行通信。JNI 提供了在 Android 应用中调用本地(C/C++)代码的能力,并允许本地代码与 Java 代码相互交互。下面是在安卓上实现OCR时用到的一些常用处理函数:
项目中的交互与实现截图:
Android 应用 JNI基本步骤:
-
C++代码: 创建 C/C++ 源代码文件,并编写所需的函数,方法或操作。使用 CMake 或 ndk-build 来构建本地代码。
-
(JNI): 在 Java 中编写声明本地方法的接口,这些方法将调用本地代码。使用
native
关键字声明本地方法。 -
加载本地库: 在 Java 代码中加载已编译的本地库(
.so
文件),通常使用System.loadLibrary(“lib-name")
来加载。 -
使用 JNI 调用本地方法: 通过 JNI 调用在本地代码中实现的本地方法,您可以在 Java 代码中调用这些方法来执行所需的本地操作。
以下是简单的示例:
Java 代码中的 JNI 接口:
public class MyNativeClass {
static {
System.loadLibrary("native-lib");
}
public native String myNativeMethod(); // JNI 方法的声明
}
C/C++ 本地代码示例(native-lib.cpp):
#include <jni.h>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_MyNativeClass_myNativeMethod(JNIEnv* env, jobject /* this */) {
return env->NewStringUTF("Hello from JNI!");
}
一、 字串互转
1. jstring转std::string
std::string jstringTostring(JNIEnv *env, jstring input)
{
char *str = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("utf-8");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray) env->CallObjectMethod(input, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0) {
str = (char *) malloc(alen + 1);
memcpy(str, ba, alen);
str[alen] = 0;
}
env->ReleaseByteArrayElements(barr, ba, 0);
std::string ret = str;
return ret;
}
2. std::string转jstring
jstring stringToJstring(JNIEnv *env,std::string &str)
{
return env->NewStringUTF(str.c_str());
}
二、图像互转
1.Bitmap转cv::Mat
将 Android 中的 Bitmap 对象转换为 OpenCV 的 cv::Mat 实现方式:
- 获取 Bitmap 信息: 使用
AndroidBitmap_getInfo()
函数获取 Bitmap 的信息,包括高度、宽度和像素格式。 - 锁定 Bitmap 像素: 使用
AndroidBitmap_lockPixels()
锁定 Bitmap 像素以访问像素数据。 - 创建 cv::Mat 对象: 使用
cv::Mat
构造函数创建一个与 Bitmap 数据对应的 cv::Mat 对象,确保使用与 Bitmap 相同的宽度、高度和像素格式。在构造函数中,传递 Bitmap 的高度、宽度、图像类型以及像素数据的指针。 - 解锁 Bitmap: 使用
AndroidBitmap_unlockPixels()
解锁 Bitmap 像素。
确保在 JNI 中正确地调用此函数,并在使用完毕后释放返回的 cv::Mat
对象以避免内存泄漏。
void bitmapToMat(JNIEnv *env, jobject bitmap, Mat &dst) {
AndroidBitmapInfo info;
void *pixels = 0;
try {
LOGI("nBitmapToMat");
CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
info.format == ANDROID_BITMAP_FORMAT_RGB_565);
CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
CV_Assert(pixels);
dst.create(info.height, info.width, CV_8UC4);
if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
LOGI("nBitmapToMat: RGBA_8888 -> CV_8UC4");
Mat tmp(info.height, info.width, CV_8UC4, pixels);
//if (needUnPremultiplyAlpha) cvtColor(tmp, dst, COLOR_mRGBA2RGBA);
//else
tmp.copyTo(dst);
} else {
// info.format == ANDROID_BITMAP_FORMAT_RGB_565
LOGI("nBitmapToMat: RGB_565 -> CV_8UC4");
Mat tmp(info.height, info.width, CV_8UC2, pixels);
cvtColor(tmp, dst, COLOR_BGR5652RGBA);
}
AndroidBitmap_unlockPixels(env, bitmap);
return;
} /*catch (const cv::Exception &e) {
AndroidBitmap_unlockPixels(env, bitmap);
LOGE("nBitmapToMat caught cv::Exception: %s", e.what());
jclass je = env->FindClass("java/lang/Exception");
if (!je) je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, e.what());
return;
}*/ catch (...) {
AndroidBitmap_unlockPixels(env, bitmap);
LOGE("nBitmapToMat caught unknown exception (...)");
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, "Unknown exception in JNI code {nBitmapToMat}");
return;
}
}
2.cv::Mat转Bitmap
- 创建 Bitmap 对象: 使用 Android 的
Bitmap
创建方法,根据cv::Mat
的像素格式和尺寸创建一个对应的 Bitmap 对象。 - 锁定 Bitmap 像素: 使用
AndroidBitmap_lockPixels()
锁定 Bitmap 像素以访问像素数据。 - 将 cv::Mat 数据复制到 Bitmap: 将
cv::Mat
数据复制到 Bitmap 对象中。 - 解锁 Bitmap: 使用
AndroidBitmap_unlockPixels()
解锁 Bitmap 像素。
void matToBitmap(JNIEnv *env, cv::Mat &src, jobject bitmap) {
AndroidBitmapInfo info;
void *pixels = 0;
try {
LOGI("nMatToBitmap");
CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
info.format == ANDROID_BITMAP_FORMAT_RGB_565);
CV_Assert(src.dims == 2 && info.height == (uint32_t) src.rows &&
info.width == (uint32_t) src.cols);
CV_Assert(src.type() == CV_8UC1 || src.type() == CV_8UC3 || src.type() == CV_8UC4);
CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
CV_Assert(pixels);
if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
Mat tmp(info.height, info.width, CV_8UC4, pixels);
if (src.type() == CV_8UC1) {
LOGI("nMatToBitmap: CV_8UC1 -> RGBA_8888");
cvtColor(src, tmp, COLOR_GRAY2RGBA);
} else if (src.type() == CV_8UC3) {
LOGI("nMatToBitmap: CV_8UC3 -> RGBA_8888");
cvtColor(src, tmp, COLOR_RGB2RGBA);
} else if (src.type() == CV_8UC4) {
LOGI("nMatToBitmap: CV_8UC4 -> RGBA_8888");
//if (needPremultiplyAlpha) cvtColor(src, tmp, COLOR_RGBA2mRGBA);
//else
src.copyTo(tmp);
}
} else {
// info.format == ANDROID_BITMAP_FORMAT_RGB_565
Mat tmp(info.height, info.width, CV_8UC2, pixels);
if (src.type() == CV_8UC1) {
LOGI("nMatToBitmap: CV_8UC1 -> RGB_565");
cvtColor(src, tmp, COLOR_GRAY2BGR565);
} else if (src.type() == CV_8UC3) {
LOGI("nMatToBitmap: CV_8UC3 -> RGB_565");
cvtColor(src, tmp, COLOR_RGB2BGR565);
} else if (src.type() == CV_8UC4) {
LOGI("nMatToBitmap: CV_8UC4 -> RGB_565");
cvtColor(src, tmp, COLOR_RGBA2BGR565);
}
}
AndroidBitmap_unlockPixels(env, bitmap);
return;
} /*catch (const cv::Exception &e) {
AndroidBitmap_unlockPixels(env, bitmap);
LOGE("nMatToBitmap caught cv::Exception: %s", e.what());
jclass je = env->FindClass("java/lang/Exception");
if (!je) je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, e.what());
return;
}*/ catch (...) {
AndroidBitmap_unlockPixels(env, bitmap);
LOGE("nMatToBitmap caught unknown exception (...)");
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");
return;
}
}
3.C++中创建Bitmap
jobject generateBitmap(JNIEnv *env, uint32_t width, uint32_t height)
{
jclass bitmapCls = env->FindClass("android/graphics/Bitmap");
jmethodID createBitmapFunction = env->GetStaticMethodID(bitmapCls,
"createBitmap",
"(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
jstring configName = env->NewStringUTF("ARGB_8888");
jclass bitmapConfigClass = env->FindClass("android/graphics/Bitmap$Config");
jmethodID valueOfBitmapConfigFunction = env->GetStaticMethodID(
bitmapConfigClass, "valueOf",
"(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;");
jobject bitmapConfig = env->CallStaticObjectMethod(bitmapConfigClass,
valueOfBitmapConfigFunction, configName);
jobject newBitmap = env->CallStaticObjectMethod(bitmapCls,createBitmapFunction,
width,
height, bitmapConfig);
return newBitmap;
}
4.Java传送图像到JNI层
java层实现图像读取与发送:
private void initTemplate() throws IOException
{
//用io流读取二进制文件,最后存入到byte[]数组中
InputStream in = getAssets().open("template.jpg");//证件模型文件
int length = in.available();
byte[] buffer = new byte[length];
in.read(buffer);
//转换为Bitmap
BitmapFactory.Options opts = new BitmapFactory.Options();
Bitmap template = BitmapFactory.decodeByteArray(buffer, 0, buffer.length, opts);
int width = template.getWidth();
int height = template.getHeight();
int[] pixArr = new int[width*height];
template.getPixels(pixArr,0,width,0,0,width,height);
int rt = scan_jia_sim.sendTemplate(pixArr,width,height);
}
JNI层实现接=收:
extern "C"
JNIEXPORT int JNICALL
Java_com_dashu_scanjia_ScanJiaSim_sendTemplate(JNIEnv *env, jobject instance, jintArray pix_, jint w, jint h)
{
jint *pix = env->GetIntArrayElements(pix_, NULL);
if (pix == NULL)
{
return -1;
}
//将c++图片转成Opencv图片
cv::Mat cv_temp(h, w, CV_8UC4, (unsigned char *) pix);
if(cv_temp.empty())
{
return -2;
}
cv_template = cv_temp.clone();
return 0;
}
文章来源:https://blog.csdn.net/matt45m/article/details/134855415
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!