【问题标题】:How to build project with jni without the building process recreating the header?如何在没有构建过程重新创建标题的情况下使用 jni 构建项目?
【发布时间】:2019-09-28 18:12:54
【问题描述】:

我在 Eclipse 中为 JNI 使用本教程:

https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html#zz-2.6

(我只使用“2.6 JNI in Eclipse”部分)。

该教程中的示例 (HelloJNI) 对我有用,也适用于我的项目。

然后我对头文件进行了一项更改-我添加了以下行:

#include <vector>

并按照教程中的说明重新构建它:

运行目标“all”的makefile,通过右键单击makefile⇒Make>Targets⇒Build⇒选择目标“all”⇒Build

它用原来的头文件替换了我的头文件......

我不明白为什么会这样,因为目标SPImageProc.h的依赖是SPImageProc.class,而我没有改变SPImageProc.class,我只改变了SPImageProc.h。

开发环境

+Eclipse IDE for Java Developers(32 位)版本:Kepler Service Release 2。

+Eclipse 的 CDT 插件

+Windows 10 64 位(我使用 eclipse 32 位,因为在某些时候,64 位 eclipse 无法打开,解决方案是使用 32 位 eclipse)

制作文件

# Define a variable for classpath
CLASS_PATH = ../bin

# Define a virtual path for .class in the bin directory
vpath %.class $(CLASS_PATH)

all : spimageproc.dll

# $@ matches the target, $< matches the first dependency
spimageproc.dll : SPImageProc.o
    g++ -Wl,--add-stdcall-alias -shared -o $@ $<

# $@ matches the target, $< matches the first dependency
SPImageProc.o : SPImageProc.cpp SPImageProc.h
    g++ -I"C:\Program Files (x86)\Java\jdk1.8.0_212\include" -I"C:\Program Files (x86)\Java\jdk1.8.0_212\include\win32" -c $< -o $@

# $* matches the target filename without the extension
SPImageProc.h : SPImageProc.class
    javah -classpath $(CLASS_PATH) $*

clean :
    rm SPImageProc.h SPImageProc.o spimageproc.dll

SPImageProc.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class SPImageProc */

#ifndef _Included_SPImageProc
#define _Included_SPImageProc
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     SPImageProc
 * Method:    cppFunc
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_SPImageProc_cppFunc
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

SPImageProc.cpp

#include <jni.h>
#include <stdio.h>
#include "SPImageProc.h"

JNIEXPORT void JNICALL Java_SPImageProc_cppFunc(JNIEnv *env, jobject thisObj) {
   printf("After adding include vector to header !\n");
   return;
}

SPImageProc.java

public class SPImageProc {

    static {
        System.loadLibrary("spimageproc"); // spimageproc.dll

    }

    // Declare native method
    private native void cppFunc();

    public static void function() {
        new SPImageProc().cppFunc(); // Allocate an instance and invoke the native
                                    // method
    }
}

CBIR.java

public class CBIR {


   public static void main(String[] args) {
      SPImageProc.function();
   }

}

编辑

这些是我想使用的原始文件(它们是我决定使用 jni 的原因):

SPImageProc.cpp

#include <cstdlib>
#include <cassert>
#include <cstring>
#include <opencv2/xfeatures2d.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <cstdio>
#include "SPImageProc.h"
extern "C" {
#include "SPLogger.h"
}

using namespace cv;
using namespace std;

#define PCA_MEAN_STR "mean"
#define PCA_EIGEN_VEC_STR "e_vectors"
#define PCA_EIGEN_VAL_STR "e_values"
#define STRING_LENGTH 1024
#define WARNING_MSG_LENGTH 2048

#define GENERAL_ERROR_MSG "An error occurred"
#define PCA_DIM_ERROR_MSG "PCA dimension couldn't be resolved"
#define PCA_FILE_NOT_EXIST "PCA file doesn't exist"
#define PCA_FILE_NOT_RESOLVED "PCA filename couldn't be resolved"
#define NUM_OF_IMAGES_ERROR "Number of images couldn't be resolved"
#define NUM_OF_FEATS_ERROR "Number of features couldn't be resolved"
#define MINIMAL_GUI_ERROR "Minimal GUI mode couldn't be resolved"
#define IMAGE_PATH_ERROR "Image path couldn't be resolved"
#define IMAGE_NOT_EXIST_MSG ": Images doesn't exist"
#define MINIMAL_GUI_NOT_SET_WARNING "Cannot display images in non-Minimal-GUI mode"
#define ALLOC_ERROR_MSG "Allocation error"
#define INVALID_ARG_ERROR "Invalid arguments"





void sp::ImageProc::initFromConfig(const SPConfig config) {
    SP_CONFIG_MSG msg = SP_CONFIG_SUCCESS;
    pcaDim = spConfigGetPCADim(config, &msg);
    if (msg != SP_CONFIG_SUCCESS) {
        spLoggerPrintError(PCA_DIM_ERROR_MSG, __FILE__, __func__, __LINE__);
        throw Exception();
    }
    numOfImages = spConfigGetNumOfImages(config, &msg);
    if (msg != SP_CONFIG_SUCCESS) {
        spLoggerPrintError(NUM_OF_IMAGES_ERROR, __FILE__, __func__, __LINE__);
        throw Exception();
    }
    numOfFeatures = spConfigGetNumOfFeatures(config, &msg);
    if (msg != SP_CONFIG_SUCCESS) {
        spLoggerPrintError(NUM_OF_FEATS_ERROR, __FILE__, __func__, __LINE__);
        throw Exception();
    }
    minimalGui = spConfigMinimalGui(config, &msg);
    if (msg != SP_CONFIG_SUCCESS) {
        spLoggerPrintError(MINIMAL_GUI_ERROR, __FILE__, __func__, __LINE__);
        throw Exception();
    }
}

void sp::ImageProc::getImagesMat(vector<Mat>& images, const SPConfig config) {
    char warningMSG[WARNING_MSG_LENGTH] = { '\0' };
    for (int i = 0; i < numOfImages; i++) {
        char imagePath[STRING_LENGTH + 1] = { '\0' };
        if (spConfigGetImagePath(imagePath, config, i) != SP_CONFIG_SUCCESS) {
            spLoggerPrintError(IMAGE_PATH_ERROR, __FILE__, __func__, __LINE__);
            throw Exception();
        }
        Mat img = imread(imagePath, IMREAD_GRAYSCALE);
        if (img.empty()) {
            sprintf(warningMSG, "%s %s", imagePath, IMAGE_NOT_EXIST_MSG);
            spLoggerPrintWarning(warningMSG, __FILE__, __func__, __LINE__);
            continue;
        }
        images.push_back(img);
    }
}

void sp::ImageProc::getFeatures(vector<Mat>& images, Mat& features) {
    //To store the keypoints that will be extracted by SIFT
    vector<KeyPoint> keypoints;
    //To store the SIFT descriptor of current image
    Mat descriptor;
    //To store all the descriptors that are extracted from all the images.

    //The SIFT feature extractor and descriptor
    Ptr<xfeatures2d::SiftDescriptorExtractor> detector =
            xfeatures2d::SIFT::create(numOfFeatures);

    //feature descriptors and build the vocabulary
    for (int i = 0; i < static_cast<int>(images.size()); i++) {
        //detect feature points
        detector->detect(images[i], keypoints);
        //compute the descriptors for each keypoint
        detector->compute(images[i], keypoints, descriptor);
        //put the all feature descriptors in a single Mat object
        features.push_back(descriptor);
    }
}

void sp::ImageProc::preprocess(const SPConfig config) {
    try {
        vector<Mat> images;
        Mat features;
        char pcaPath[STRING_LENGTH + 1] = { '\0' };
        getImagesMat(images, config);
        getFeatures(images, features);
        pca = PCA(features, Mat(), CV_PCA_DATA_AS_ROW, pcaDim);
        if (spConfigGetPCAPath(pcaPath, config) != SP_CONFIG_SUCCESS) {
            spLoggerPrintError(PCA_FILE_NOT_RESOLVED, __FILE__, __func__,
            __LINE__);
            throw Exception();
        }
        FileStorage fs(pcaPath, FileStorage::WRITE);
        fs << PCA_EIGEN_VEC_STR << pca.eigenvectors;
        fs << PCA_EIGEN_VAL_STR << pca.eigenvalues;
        fs << PCA_MEAN_STR << pca.mean;
        fs.release();
    } catch (...) {
        spLoggerPrintError(GENERAL_ERROR_MSG, __FILE__, __func__, __LINE__);
        throw Exception();
    }
}

void sp::ImageProc::initPCAFromFile(const SPConfig config) {
    if (!config) {
        spLoggerPrintError(GENERAL_ERROR_MSG, __FILE__, __func__, __LINE__);
        throw Exception();
    }
    char pcaFilename[STRING_LENGTH + 1] = { '\0' };
    if (spConfigGetPCAPath(pcaFilename, config) != SP_CONFIG_SUCCESS) {
        spLoggerPrintError(PCA_FILE_NOT_RESOLVED, __FILE__, __func__, __LINE__);
        throw Exception();
    }
    FileStorage fs(pcaFilename, FileStorage::READ);
    if (!fs.isOpened()) {
        spLoggerPrintError(PCA_FILE_NOT_EXIST, __FILE__, __func__, __LINE__);
        throw Exception();
    }
    fs[PCA_EIGEN_VEC_STR] >> pca.eigenvectors;
    fs[PCA_EIGEN_VAL_STR] >> pca.eigenvalues;
    fs[PCA_MEAN_STR] >> pca.mean;
    fs.release();
}

sp::ImageProc::ImageProc(const SPConfig config) {
    try {
        if (!config) {
            spLoggerPrintError(INVALID_ARG_ERROR, __FILE__, __func__, __LINE__);
            throw Exception();
        }
        SP_CONFIG_MSG msg;
        bool preprocMode = false;
        initFromConfig(config);
        if ((preprocMode = spConfigIsExtractionMode(config, &msg))) {
            preprocess(config);
        } else {
            initPCAFromFile(config);
        }
    } catch (...) {
        spLoggerPrintError(GENERAL_ERROR_MSG, __FILE__, __func__, __LINE__);
        throw Exception();
    }
}

SPPoint* sp::ImageProc::getImageFeatures(const char* imagePath, int index,
        int* numOfFeats) {
    vector<KeyPoint> keypoints;
    Mat descriptor, img, points;
    double* pcaSift = NULL;
    char errorMSG[STRING_LENGTH * 2];
    Ptr<xfeatures2d::SiftDescriptorExtractor> detector;
    if (!imagePath || !numOfFeats) {
        spLoggerPrintError(INVALID_ARG_ERROR, __FILE__, __func__, __LINE__);
        return NULL;
    }
    img = imread(imagePath, IMREAD_GRAYSCALE);
    if (img.empty()) {
        sprintf(errorMSG, "%s %s", imagePath, IMAGE_NOT_EXIST_MSG);
        spLoggerPrintError(errorMSG, __FILE__, __func__, __LINE__);
        return NULL;
    }
    detector = xfeatures2d::SIFT::create(numOfFeatures);
    detector->detect(img, keypoints);
    detector->compute(img, keypoints, descriptor);
    points = pca.project(descriptor);
    pcaSift = (double*) malloc(sizeof(double) * pcaDim);
    if (!pcaSift) {
        spLoggerPrintError(ALLOC_ERROR_MSG, __FILE__, __func__, __LINE__);
        return NULL;
    }
    *numOfFeats = points.rows;
    SPPoint* resPoints = (SPPoint*) malloc(sizeof(*resPoints) * points.rows);
    if (!resPoints) {
        free(pcaSift);
        spLoggerPrintError(ALLOC_ERROR_MSG, __FILE__, __func__, __LINE__);
        return NULL;
    }
    for (int i = 0; i < points.rows; i++) {
        for (int j = 0; j < points.cols; j++) {
            pcaSift[j] = (double) points.at<float>(i, j);
        }
        resPoints[i] = spPointCreate(pcaSift, pcaDim, index);
    }
    free(pcaSift);
    return resPoints;
}

void sp::ImageProc::showImage(const char* imgPath) {
    if (minimalGui) {
        Mat img = imread(imgPath, cv::IMREAD_COLOR);
        if (img.empty()) {
            spLoggerPrintWarning(IMAGE_NOT_EXIST_MSG, __FILE__, __func__,
            __LINE__);
            return;
        }
        imshow(windowName, img);
        waitKey(0);
        destroyAllWindows();
    } else {
        spLoggerPrintWarning(MINIMAL_GUI_NOT_SET_WARNING, __FILE__, __func__,
        __LINE__);
    }

}


SPImageProc.h

#ifndef SPIMAGEPROC_H_
#define SPIMAGEPROC_H_
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <vector>

extern "C" {
#include "SPConfig.h"
#include "SPPoint.h"
}

namespace sp {

/**
 * A class which supports different image processing functionalites.
 */
class ImageProc {
private:
    const char* windowName = "Software Project CBIR";
    int pcaDim;
    int numOfImages;
    int numOfFeatures;
    cv::PCA pca;
    bool minimalGui;
    void initFromConfig(const SPConfig);
    void getImagesMat(std::vector<cv::Mat>&, const SPConfig);
    void getFeatures(std::vector<cv::Mat>&,
            cv::Mat&);
    void preprocess(const SPConfig config);
    void initPCAFromFile(const SPConfig config);
public:

    /**
     * Creates a new object for the purpose of image processing based
     * on the configuration file.
     * @param config - the configuration file from which the object is created
     */
    ImageProc(const SPConfig config);

    /**
     * Returns an array of features for the image imagePath. All SPPoint elements
     * will have the index given by index. The actual number of features extracted
     * for this image will be stored in the pointer given by numOfFeats.
     *
     * @param imagePath - the target imagePath
     * @param index - the index  of the image in the database
     * @param numOfFeats - a pointer in which the actual number of feats extracted
     *                     will be stored
     * @return
     * An array of the actual features extracted. NULL is returned in case of
     * an error.
     */
    SPPoint* getImageFeatures(const char* imagePath,int index,int* numOfFeats);

    /**
     *  Displays the image given by imagePath. Notice that this function works
     *  only in MinimalGUI mode (otherwise a warnning message is printed).
     *
     *  @param imagePath - the path of the image to be displayed
     */
    void showImage(const char* imagePath);
};

}
#endif

【问题讨论】:

  • 您编辑了哪个标题?肯定不是SPImageProc.h,其突出的“请勿编辑此文件”评论就在顶部?
  • 哦.. 我编辑了 SPImageProc.h 。那么如何更改该标题?我必须更改它,因为我使用 jni 的原因是因为我有一个 cpp 文件 (SPImageProc.cpp) 和对应的头文件 (SPImageProc.h) 已经准备好并测试了计算机视觉功能,我想使用它们.
  • 不必根据javah从当前版本的类文件生成的内容修改该标题。绝对没有必要。期间。
  • 请注意 javah 已过时,not part of the up-to-date JDK。 Java10 使用 javac -h 代替。

标签: java c++ eclipse makefile java-native-interface


【解决方案1】:

您提到更改“the”头文件,实际上您只提供了一个,而您的 makefile 只引用了那个。该标头是从您的类文件机器生成的,不应编辑,如顶部突出的注释所示。如果您以影响本机接口的方式修改类,即添加或删除本机方法或修改任何现有方法的签名,则需要重新生成它。

这就是为什么您复制的 Makefile 设置为在 Java 类文件更改时自动重建头文件的原因。当然,如果 Java 源代码发生变化,它也会自动重建类文件,我想这不会那么意外。所以,

如何在构建过程不重新创建header的情况下使用jni构建项目?

如果您不修改 Java 源代码或出于其他原因重新构建 Java 类,则不会重新构建标头。您还可以删除导致在 Java 部件发生更改时重新构建它的 makefile 规则,但是您只需要手动维护它,如果您通过运行 javah 来维护它 - 迄今为止最简单和最安全替代方案——然后你回到你开始的地方,但自动化程度较低。

此外,没有理由修改该标题。它已经包含了其中的声明所需的所有内容,因此您无法为其本身添加任何好处。您想在 C 源代码中声明的任何其他内容都可以直接进入源代码本身或进入您手动创建和管理的单独标头。

【讨论】:

  • 非常感谢。现在我编辑了帖子 - 我在帖子末尾添加了我想要使用的原始标题和 cpp 文件。所以我只是将其中的所有内容复制到我现有的 SPImageProc.cpp 文件中?
  • 你可以这样做@Moshe,是的。您也可以只重命名两个标头之一(如果您选择 JNI 标头,那么您还需要相应地更改生成文件)。头文件名和源文件名之间完全没有任何对应关系的要求,原则上一个给定的源文件可以包含多少个头文件。
  • 太棒了!跟进问题:现在我尝试为openv添加包含,为了在eclipse中配置openv,我需要在“工具设置”选项卡中配置它。但是这个标签不见了。有人问过这个问题(这里:stackoverflow.com/questions/17687984/…),他得到的解决方案是检查“自动生成 Makefiles”。但我担心它会产生问题......你怎么看?
  • 我认为这应该是一个单独的问题,但我同意你的担忧。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-04
  • 2014-02-20
  • 2015-01-21
  • 1970-01-01
  • 2023-03-16
  • 2014-06-21
  • 1970-01-01
相关资源
最近更新 更多