Android JUnit test with OpenCV
This article shows how to do image matching in android JUnit test.
The following shows the method to search for a second image in a first image and return a simularity percentage.
- Import OpenCV to android
- Code to do the comparing
-
Improve code
Thanks to all of them, saved my time.
1.1 Download the latest OpenCV sdk from OpenCV.org, choose "Android pack", decompress the zip file to local.
1.2 Import the OpenCV to Android Studio, in the project, from "File -> New -> Import Module", choose "sdk/java" folder in OpenCV android sdk folder.
1.3 Update "build.gradle" under the "imported module" to update the 4 fields to match the "app/build.gradle"
a. compileSdkVersion
b. buildToolsVersion
c. minSdkVersion
d. targetSdkVersion
1.4 Add module dependency by "File -> Project Structure", and in "app" select the "Dependencies", click "+" on the top right corner, choose "Module Dependency" and select the imported OpenCV module.
1.5 Copy libs folder under "sdk/native" to android studio under "app/src/main".
1.6 Under "app/src/main/libs", remove the unnecessary folders according to your platform. In this case, I removed all folders except for "armeabi" and "armeabi-v7a".
Then remove the unnecessary files from the remaining folders under "app/src/main/libs/", in my case, I removed all files except for the one "libopencv_java3.so" in the two remaining folders.
1.7 In android studio, rename the copied libs directory to 'jniLibs'.
This step is very important, since Android Studio expects native libs in app/src/main/jniLibs
instead of libs
folder. And the new folder name MUST exactly be jniLibs
.
1.8 In the test class, add the following code, and we can get the result from the log:
private static String TAG = "Test";
static {
System.loadLibrary("opencv_java3");
}
static {
if (!OpenCVLoader.initDebug()){
Log.w(TAG, "static initializer: Fail to load opencv libs !");
Log.w(TAG, "static initializer: Thus fail the case.");
fail();
} else {
Log.i(TAG, "static initializer: Succeed to load opencv libs !");
}
}
If we get the log "static initializer: Succeed to load opencv libs !", then the whole importing thing is done!
-----------------------------------------------------------------------------------------
The first stage is done.
The second and the third stages are shown below :
private static final double THRESHOLD = 0.95;
private static String TAG = "Test";
Boolean matchImages(Mat img, Mat template, String outFile, String suffix) throws IOException {
Log.i(TAG, "matchImages: Run image match method complex version.");
outFile += suffix;
if (new File(outFile).exists()){
Boolean flag = new File(outFile).delete();
Log.i(TAG, "matchImages: The result file already exists , delete it now : " + flag);
}
//Mat img = Utils.loadResource(mContext, inFileID, Imgcodecs.CV_LOAD_IMAGE_COLOR);
if (!img.empty()){
Log.i(TAG, "matchImages: Load source image succeed !");
} else {
Log.w(TAG, "matchImages: Fail to load source image !!!");
}
//Mat template = Utils.loadResource(mContext, templateFileID, Imgcodecs.CV_LOAD_IMAGE_COLOR);
if (!template.empty()) {
Log.i(TAG, "matchImages: Load template image succeed !");
} else {
Log.w(TAG, "matchImages: Fail to load template image !!!");
}
int result_cols = img.cols() - template.cols() + 1;
int result_rows = img.rows() - template.rows() + 1;
Mat result = new Mat(result_rows, result_cols, CvType.CV_32FC1);
//NEVER do the matching and NOT normalize
Imgproc.matchTemplate(img, template, result, Imgproc.TM_CCOEFF_NORMED);
//Localize the best match with minMaxLoc
Core.MinMaxLocResult mmr = Core.minMaxLoc(result);
Point matchLoc;
matchLoc = mmr.maxLoc;
Log.i(TAG, "Match percentage : " + mmr.maxVal);
//Draw the rectangle.
Imgproc.rectangle(img, matchLoc, new Point(matchLoc.x + template.cols(), matchLoc.y + template.rows()),
new Scalar(0, 255, 0), 8);
//Save the visualized detection
Log.i(TAG, "matchImages: Write to " + outFile);
Imgcodecs.imwrite(outFile, img);
if (mmr.maxVal >= THRESHOLD) {
Log.i(TAG, "matchImages: Match percentage is above the THRESHOLD.");
Log.i(TAG, "matchImages: ###### Found match . ######");
return true;
} else {
Log.w(TAG, "matchImages: Match percentage is below the THRESHOLD!!!");
Log.w(TAG, "matchImages: ****** None match . ******");
return false;
}
}
As suggested, certain points MUST be noticed:
a. The matching method should better be "Imgproc.TM_CCOEFF_NORMED".
b. With this matching method, "mmr.maxVal" returns the highest simularity percentage of the images.
c. The following code should NOT be applied if we'd like to get a proper percentage:
Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
d. The format of the two Mat images MUST be exactly the same. That is :
Mat.channels()
Mat.type()
If not, android studio is likely to throw an error.
Transfer one to match the other before do image matching.
Refer to:
- Reduce false detection of template matching
- Calculation of template matching
- Template Matching
- Template Matching is wrong with specific Reference image
- Opencv in android
And that's all.