【问题标题】:How to return a vector<Point> from a jni C++ function?如何从 jni C++ 函数返回 vector<Point>?
【发布时间】:2019-06-21 06:22:56
【问题描述】:

我正在开发一个使用 Opencv 处理图像的 android 项目。 我写了一个 android jni 函数,它应该返回一个向量,但我不知道如何正确地做到这一点。

我尝试将矢量转换为 jobjectArray,但它不起作用。 这是我正在处理的代码:

    jobjectArray
    Java_com_grimg_testtt_MainActivity_getQuadrilateral(
    JNIEnv *env,
    jobject /* this */,
    cv::Mat & grayscale,
    cv::Mat & output) {
std::vector<std::string> vec;
cv::Mat approxPoly_mask(grayscale.rows, grayscale.cols, CV_8UC1);
approxPoly_mask = cv::Scalar(0);
std::vector<std::vector<cv::Point>> contours;
std::vector<int> indices(contours.size());
std::iota(indices.begin(), indices.end(), 0);
sort(indices.begin(), indices.end(), [&contours](int lhs, int rhs) {
    return contours[lhs].size() > contours[rhs].size();
});
/// Find the convex hull object for each contour
std::vector<std::vector<cv::Point>> hull(1);
cv::convexHull(cv::Mat(contours[indices[0]]), hull[0], false);

std::vector<std::vector<cv::Point>> polygon(1);
approxPolyDP(hull[0], polygon[0], 20, true);
drawContours(approxPoly_mask, polygon, 0, cv::Scalar(255));
//imshow("approxPoly_mask", approxPoly_mask);

if (polygon[0].size() >= 4) // we found the 4 corners
{
    return(polygon[0]);
}

return(std::vector<cv::Point>());

}

在最后两行中,我收到了这个很明显的错误:

    Returning 'std::vector<cv::Point> &' from a function returning 'jobjectArray': Types 'jobjectArray' and 'std::vector<cv::Point>' are not compatible.

我能做些什么来克服这个问题?

编辑:

jclass clazz = (*env).FindClass("java/util/ArrayList");
jobjectArray result = (*env).NewObjectArray(polygon[0].size(), clazz, 0);

if (polygon[0].size() >= 4) // we found the 4 corners
{
    for (int n=0;n<polygon[0].size();n++)
    {
        cv::Point point = (cv::Point) static_cast<cv::Point>(polygon[0][n]);
        (*env).CallVoidMethod(result, (*env).GetMethodID(clazz, "add", "(java/lang/Object)V"), point);

    }
    return result;
}

return result;

}

编辑 2:

    jclass ptCls = env->FindClass("java/awt/Point");
jobjectArray result = (*env).NewObjectArray(polygon[0].size(), ptCls, NULL);

if (result == NULL) return NULL;

if (polygon[0].size() >= 4) // we found the 4 corners
{
    for (int n=0;n<polygon[0].size();n++)
    {
        jobject point = (jobject) static_cast<jobject>(polygon[0][n]);
        //(*env).CallVoidMethod(result, (*env).GetMethodID(ptCls, "add", "(java/lang/Object)V"), polygon[0][n]);
        (*env).SetObjectArrayElement(result, polygon[0].size(), point);

    }
    return result;
}

return result;

错误

    error: cannot cast from type 'std::__ndk1::__vector_base<cv::Point_<int>, std::__ndk1::allocator<cv::Point_<int> > >::value_type' (aka 'cv::Point_<int>') to pointer type 'jobject' (aka '_jobject *')

        jobject point = (jobject) static_cast<jobject>(polygon[0][n]);

【问题讨论】:

  • 在您尝试进行转换的地方发布代码。目前,您只是要求我们为您编写该代码。你需要展示你尝试过的东西。
  • 谢谢先生的回复,我尝试了很多解决方案,但没有任何效果,所以我删除了该代码,但我知道我应该在 C++ 中为 Vector 创建一个包装器,我希望你能帮助我。
  • 尽你最大的努力。如果你展示你的尝试,你更有可能得到答案。
  • 我猜函数返回类型是 std::vector&lt;cv::Point&gt; 而你返回的是 std::vector&lt;cv::Point&gt;&amp;
  • 你应该函数签名吗?

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


【解决方案1】:

在 JNI 层中,您应该将本机对象映射到 Java 对象(Java 对象分配在 JVM 堆上)。

cv::Point需要转换成Java类,std::vector需要转换成jobjectArray

使用(*env)-&gt;NewObjectArray 像这样创建jobjectArray

jobjectArray result = (*env)->NewObjectArray(env, size, PointCls, NULL);
if (result == NULL) return NULL;

PointCls 应指与您的本机 Point 类对应的 Java 类。

然后遍历每个原生 cv::Point 对象,create 从中获取一个 Java Point,复制字段,并将其放入数组中(使用 (*env)-&gt;SetObjectArrayElement)。

然后就可以返回result数组了。

例如这样:

std::vector<cv::Point> const& input = polygon[0];
jclass doubleArray = env->FindClass("[D");
if (doubleArray == NULL) return NULL;
jobjectArray result = env->NewObjectArray(input.size(), doubleArray, NULL);
if (result == NULL) return NULL;
for (int i = 0; i < input.size(); ++i) {
    jdoubleArray element = env->NewDoubleArray(2);
    if (element == NULL)
        break;
    jdouble buf[2] = { input[i].x, input[i].y };
    env->SetDoubleArrayRegion(element, 0, 2, buf);
    env->SetObjectArrayElement(result, i, element);
}
return result;

这将返回一个二维数组double[][],其中xy 对应于第二维中的01

【讨论】:

  • 如果没有 Java Point 等效项很方便,您也可以将 cv::Point 向量展平为形状为 {x1, y1, x2, y2, ...} 的 JNI double[]
  • 谢谢先生的回答,实际上这就是我想要做的。我会继续努力的。
  • @rustyx 我尝试将您的回答说明转换为代码,但出现错误,请查看编辑 2。
  • cv::Point 是一个原生对象,你不能将它添加到Java数组中,对于每个点你仍然需要创建一个新的Java对象并手动复制点值(x,y)给它。 java.awt.Point 有两个 int,但你需要两个 double,所以它不起作用。也许为每个点创建一个 2 元素双精度数组,并返回一个二维数组:double[][]Example.
  • 我正在尝试从点的 x 和 y 值创建一个 jstring 对象,并用“,”分隔它们,然后在 java 中我提取每个 x 和 y 并重新创建点。我正在尝试这个,因为我想尝试图像处理方法然后我将优化代码。您建议如何将字符串转换为 jstring ?
猜你喜欢
  • 1970-01-01
  • 2015-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-18
相关资源
最近更新 更多