【问题标题】:How to display Mat image on android emulator? Using NDK如何在 android 模拟器上显示 Mat 图像?使用 NDK
【发布时间】:2017-12-16 12:28:48
【问题描述】:

我想在 android 模拟器上显示一个 Mat 图像。我将assetManager 从java 传递到c++ 并将图像作为资产加载。然后我执行 AAsset_read,并将图像数据保存到 char* 缓冲区,并使用 cv::imdecode 转换为 Mat。 从这里开始,我该如何将 Mat 图像添加到模拟器中?

在默认的stringFromJNI 方法中,他们似乎使用了显示文本的return->envNewStringUTF(hello.c_str());

Java_com_example_user_project_MainActivity_stringFromJNI(JNIEnv *env, jobject /* this */) {

std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());

那么我该如何处理 Mat 图像呢?

编辑:这是我的 C++ 代码:

JNIEXPORT jintArray JNICALL     Java_com_example_user_activity_MainActivity_generateAssets(JNIEnv* env,jobject thiz,jobject assetManager) {

AAsset* img;

AAssetManager *mgr = AAssetManager_fromJava(env, assetManager);
AAssetDir* assetDir = AAssetManager_openDir(mgr, "");
const char* filename;
while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL) {
    AAsset *asset = AAssetManager_open(mgr, filename, AASSET_MODE_UNKNOWN);

    if(strcmp(filename, "hi.jpg")==0 ) {
        img = asset;
    }
}
long sizeOfImg = AAsset_getLength(img);
char* buffer = (char*) malloc (sizeof(char)*sizeOfImg);
AAsset_read(img, buffer, sizeOfImg);


std::vector<char> data(buffer, buffer + sizeOfImg);

cv::Mat h = cv::imdecode(data, IMREAD_UNCHANGED);

jintArray resultImage = env->NewIntArray(h.total());
jint *_data = new jint[h.total()];
for (int i = 0; i < h.total(); i++) {
    char b = h.data[4 * i];
    char g = h.data[4 * i + 1];
    char r = h.data[4 * i + 2];
    char a = 255;
    _data[i] = (((jint) a << 24) & 0xFF000000) + (((jint) r << 16) & 0x00FF0000) +
               (((jint) g << 8) & 0x0000FF00) + ((jint) b & 0x000000FF);
}
env->SetIntArrayRegion(resultImage, 0, h.total(), _data);
delete[]_data;
return resultImage;

}

还有java代码:

    assetManager = getAssets();
    resultImage = generateAssets(assetManager);

    Bitmap imageinjava = Bitmap.createBitmap(resultImage, 100,100, Bitmap.Config.ARGB_8888);

    ImageView tv1;
    tv1= (ImageView) findViewById(R.id.image);
    tv1.setImageBitmap(imageinjava);

【问题讨论】:

  • “将 Mat 图像添加到模拟器”是什么意思?
  • 只显示在屏幕上。就像代码如何显示“来自 c++ 的你好”一样,我想显示图像
  • 如果您不使用 java opencv API - 最好的目标是从您的图像创建位图。您可以使用 Mat 对象的 RGBA 数据返回 jintArray,然后通过调用 Bitmap.createBitmap 创建位图
  • 对不起,我应该提到这一点 - 我正在使用 OpenCV 并且需要它作为 Mat 对象,因为我想稍后对 Mat 对象进行进一步处理。这只是一个helloworld阶段。
  • 如果你想在 java 和 c++ 之间传递原生对象,那么你可以返回指向 Mat 对象的指针。虽然(不使用 opencv java api)

标签: java android c++ opencv java-native-interface


【解决方案1】:

为了转换您的原生 Mat 图像,您可以这样做:

JNIEXPORT jintArray JNICALL  Java_com_example_user_activity_MainActivity_generateAssets(JNIEnv* env, jobject thiz, jobject assetManager) {
AAssetManager *mgr = AAssetManager_fromJava(env, assetManager);
AAsset *img = AAssetManager_open(mgr, "hi.jpg", AASSET_MODE_UNKNOWN);

long sizeOfImg = AAsset_getLength(img);
char* buffer = (char*)AAsset_getBuffer(img);

std::vector<char> data(buffer, buffer + sizeOfImg);

cv::Mat h = cv::imdecode(data, IMREAD_UNCHANGED);

jintArray resultImage = env->NewIntArray(h.total());
jint *_data = new jint[h.total()];
for (int i = 0; i < h.total(); i++) {
    char b = h.data[h.channels() * i];
    char g = h.data[h.channels() * i + 1];
    char r = h.data[h.channels() * i + 2];
    char a = 255;
    _data[i] = (((jint) a << 24) & 0xFF000000) + (((jint) r << 16) & 0x00FF0000) +
               (((jint) g << 8) & 0x0000FF00) + ((jint) b & 0x000000FF);
}
env->SetIntArrayRegion(resultImage, 0, h.total(), _data);
delete[]_data;
return resultImage;

}

在你的 java 代码中是这样的

    int[] result = generateAssets(getAssets());
    Bitmap bitmap = Bitmap.createBitmap(result, yourimagewidth, yourimageheight, Bitmap.Config.ARGB_8888);

如果你想在 c++ 和 java 之间传递原生 Mat 对象 - 返回指向你的 Mat 的指针,然后将它传递给原生函数;

Mat *mat = new Mat(100, 100);
return (jlong) mat;

【讨论】:

  • 我试图实现这个,但它不起作用,我真的不明白发生了什么。 ResultImage 正在重新调整 null。请你能解释一下你做了什么吗?谢谢。
  • 我已经编辑了我的答案。我认为您过于复杂地读取资产。该代码将从根资产文件夹(“assets/hi.jpg”)读取文件 hi.jpg 并将 jintArray 返回到 java
  • 非常感谢它有效!但是我仍然不确定你在 for 循环开头做了什么: for (int i = 0; i
  • @Ber12345 当然。由于位图构造函数需要将 RGBA 数据存储为整数(4 个字节),因此我只需遍历整个图像(h.total() = h.size().width * h.size().height)并将红色绿色和蓝色值每个像素为整数,alpha(透明度)设置为 255,这意味着没有透明度。因为我不知道图像是 4 通道还是 3 通道 - 我使用 h.channels() 来正确获取每个像素的值。如果某些事情对您来说仍然没有多大意义 - 请告诉我
  • 谢谢,这很有道理。还有一件事,如何在java端找到我的图像高度和宽度?
猜你喜欢
  • 2018-08-17
  • 1970-01-01
  • 1970-01-01
  • 2020-07-18
  • 1970-01-01
  • 1970-01-01
  • 2018-07-07
  • 2013-05-13
  • 1970-01-01
相关资源
最近更新 更多