问题
I am making an Document Scanner. I'm using opencv for image processing. In camera view i'm bounding rectangle on largest contour. Processing part written in Native-lib.cpp. It is detecting largest contour properly. And now i want to capture only boudingRect which is written in native-lib.cpp. So i want object of native-lib in java class. help to get that.
Native-lib.cpp
extern "C"
JNIEXPORT void JNICALL
Java_prisca_ctest_OpenCvCamera_doWithMat(JNIEnv *env, jobject instance, jlong matAddrGr,
jlong matAddrRgba) {
try {
Mat &image = *(Mat *) matAddrRgba;
Rect bounding_rect;
Mat thr(image.rows, image.cols, CV_8UC1);
cvtColor(image, thr, CV_BGR2GRAY); //Convert to gray
threshold(thr, thr, 150, 255, THRESH_BINARY + THRESH_OTSU); //Threshold the gray
vector<vector<Point> > contours; // Vector for storing contour
vector<Vec4i> hierarchy;
findContours(thr, contours, hierarchy, CV_RETR_CCOMP,
CV_CHAIN_APPROX_SIMPLE); // Find the contours in the image
sort(contours.begin(), contours.end(),
compareContourAreas); //Store the index of largest contour
bounding_rect = boundingRect((const _InputArray &) contours[0]);
rectangle(image, bounding_rect, Scalar(250, 250, 250) , 5);
} catch (int a) {
}
}
Activity
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.cam);
mOpenCvCameraView = (JavaCameraView) findViewById(R.id.tutorial1_activity_java_surface_view);
mOpenCvCameraView.setVisibility(View.VISIBLE);
mOpenCvCameraView.setCvCameraViewListener(this);
btnCapture = (Button) findViewById(R.id.btnCapture);
btnCapture.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String timestamp = new SimpleDateFormat("ddMMyyyy_HHmmss", Locale.US).format(new Date());
File imgFolder = new File(FILE_LOCATION);
imgFolder.mkdir();
File image = new File(imgFolder, "Scan" + timestamp + ".jpg");
String fileName = FILE_LOCATION +
"/Scan" + timestamp + ".jpg";
Toast.makeText(OpenCvCamera.this, image + " saved", Toast.LENGTH_SHORT).show();
Imgcodecs.imwrite(fileName, mRgba);
}
}) ;
}
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
// input frame has RGBA format
mRgba = inputFrame.rgba();
mGray = inputFrame.gray();
doWithMat(mGray.getNativeObjAddr(), mRgba.getNativeObjAddr());
return mRgba;
}
What should i add above Imgcodecs.imwrite(fileName, mRgba)
to crop the matrix and save only boundingRect
part?
probably I have to write this before imwrite
-
Mat cropped = mRgba.submat( bounding_rect );
Imgcodecs.imwrite(fileName, cropped);
But i cannot call bounding_rect from Native-lib. How to call it ? Thank u in advance :)
回答1:
You can receive org.opencv.core.Rect from native-lib. The easiest way is to change your native method signature, doWithMat()
like this:
private org.opencv.core.Rect mBoundingRect;
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
// input frame has RGBA format
mRgba = inputFrame.rgba();
mGray = inputFrame.gray();
mBoundingRect = doWithMat(mGray.getNativeObjAddr(), mRgba.getNativeObjAddr());
return mRgba;
}
extern "C"
JNIEXPORT jobject JNICALL
Java_prisca_ctest_OpenCvCamera_doWithMat(JNIEnv *env, jobject instance,
jlong matAddrGr, jlong matAddrRgba) {
Mat &image = *(Mat *) matAddrRgba;
Rect bounding_rect;
Mat thr(image.rows, image.cols, CV_8UC1);
cvtColor(image, thr, CV_BGR2GRAY); // Convert to gray
threshold(thr, thr, 150, 255, THRESH_BINARY + THRESH_OTSU); //Threshold the gray
vector<vector<Point> > contours; // Vector for storing contour
vector<Vec4i> hierarchy;
findContours(thr, contours, hierarchy, CV_RETR_CCOMP,
CV_CHAIN_APPROX_SIMPLE); // Find the contours in the image
sort(contours.begin(), contours.end(),
compareContourAreas); // Store the index of largest contour
bounding_rect = boundingRect((const _InputArray &) contours[0]);
rectangle(image, bounding_rect, Scalar(250, 250, 250) , 5);
jclass rectClass = env->FindClass("org/opencv/core/Rect");
jmethodID rectCtorID = env->GetMethodID(rectClass, "<init>", "(IIII)V");
return env->NewObject(rectClass, rectCtorID, bounding_rect.x, bounding_rect.y, bounding_rect.width, bounding_rect.height);
}
As you can see, I removed try ... catch
from the native code; I don't think it can really help, especially when you try to catch an int, instead of exception.
Note that extraction of rectClass and rectCtorID are expensive operations, therefore it's smart to cache these values:
static jclass rectClass = nulltr;
ststic jmethodID rectCtorID = 0;
if (rectCtorID == 0) {
rectClass = env->NewGlobalRef(env->FindClass("org/opencv/core/Rect"));
rectCtorID = env->GetMethodID(rectClass, "<init>", "(IIII)V");
}
Note that we need a global reference to Java class, but the method ID is just an int.
Another optimization that I can suggest is to use matAddrGr
. If I understand correctly, OpenCV will prepare both matrices for onCameraFrame()
, so you there is probably no need to convert RGB to gray.
回答2:
here, you have two options first one you need to process byte of the camera frame using JNI and then after you need to fetch processed pixels and set into the camera preview. Then after using canvas crop bitmap. The second one you need to follow this code for the image cropping.
Mat image = ...; // your image
Mat crop(image, bounding_rect);
// NOTE: this is only give refrence to you.
Mat output = crop.clone();
回答3:
You need to do a couple of things to be able to call Java_prisca_ctest_OpenCvCamera_doWithMat in Java code.
Do you have CMakeList.txt or something similar where you decide, what files will be part of your compilation?
If so, you can present your 'native-lib.cpp' in Java code like this:
static { System.loadLibrary("native-lib"); }
Then you have to present your native functions to Java code. It is done like this:
public native void doWithMat(long matAddrGr, long matAddrRgba);
Finally you can call your native function anywhere in the same file you have presented that function. Just call e.g.:
doWithMat(1, 2);
But like you might have noticed, doWithMath() is void type function, so it does not return anything. You just simply cant "access object" from C++ in Java code. Instead you have to have getter to transfer object to Java side or return Rect from function doWithMath().
Unfortunately you can't return Rect straight from C++ code, since you cannot specify the function to return C++ types. Instead what you should do IMHO is to get data from Rect (coordinates or whatever it has) and return them in array, and then handle that data in Java side.
I recommend to go through this hello-jni tutorial so you get the idea of how to do stuff in JNI: https://developer.android.com/ndk/samples/sample_hellojni.html
来源:https://stackoverflow.com/questions/47511956/how-to-call-jni-object-in-android-activity