How to find the local maxima and minima in an image - java

I am learning how to get the local and global maximum in an image, and as far as know, in one image there is only one global Maximum and one global minimum, and i managed to get these values and their corresponding locations in the image. so my questions are:
how to get the local maxima in an image
how to get the local minima in an image
as you see in the code below, I am using mask, but at run time i receieve the below mentioned error message. so please let me know why do we need mask and how to use it properly.
update:
Line 32 is: MinMaxLocResult s = Core.minMaxLoc(gsMat, mask);
code:
public static void main(String[] args) {
MatFactory matFactory = new MatFactory();
FilePathUtils.addInputPath(path_Obj);
Mat bgrMat = matFactory.newMat(FilePathUtils.getInputFileFullPathList().get(0));
Mat gsMat = SysUtils.rgbToGrayScaleMat(bgrMat);
Log.D(TAG, "main", "gsMat.dump(): \n" + gsMat.dump());
Mat mask = new Mat(new Size(3,3), CvType.CV_8U);//which type i should set for the mask
MinMaxLocResult s = Core.minMaxLoc(gsMat, mask);
Log.D(TAG, "main", "s.maxVal: " + s.maxVal);//to get the global maximum
Log.D(TAG, "main", "s.minVal: " + s.minVal);//to get the global minimum
Log.D(TAG, "main", "s.maxLoc: " + s.maxLoc);//to get the coordinates of the global maximum
Log.D(TAG, "main", "s.minLoc: " + s.minLoc);//to get the coordinates of the global minimum
}
error message:
OpenCV Error: Assertion failed (A.size == arrays[i0]->size) in cv::NAryMatIterator::init, file ..\..\..\..\opencv\modules\core\src\matrix.cpp, line 3197
Exception in thread "main" CvException [org.opencv.core.CvException: ..\..\..\..\opencv\modules\core\src\matrix.cpp:3197: error: (-215) A.size == arrays[i0]->size in function cv::NAryMatIterator::init
]
at org.opencv.core.Core.n_minMaxLocManual(Native Method)
at org.opencv.core.Core.minMaxLoc(Core.java:7919)
at com.example.globallocalmaxima_00.MainClass.main(MainClass.java:32)

In order to calculate global min/max values you don't need to use mask completely.
For calculating local min/max values you can do a little trick. You need to perform dilate/erode operation and then compare pixel value with values of original image. If value of original image and dilated/eroded image are equal therefore this pixel is local min/max.
The code is following:
Mat eroded = new Mat();
Mat dilated = new Mat();
Imgproc.erode(gsMat, eroded, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5,5)));
Imgproc.dilate(gsMat, dilate, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5,5)));
Mat localMin = new Mat(gsMat.size(), CvType.CV_8U, new Scalar(0));
Mat localMax = new Mat(gsMat.size(), CvType.CV_8U, new Scalar(0));
for (int i=0; i<gsMat.height; i++)
for (int j=0; j<gsMat.width; j++)
{
if (gsMat.get(i,j) == eroded.get(i,j))
localMin.put(i,j,255);
if (gsMat.get(i,j) == dilated.get(i,j))
localMax.put(i,j,255);
}
Please note, I'm not a Java programmer. So, code is only illustration of algorithm.

Related

Modify Mat object Pixel values by using OpenCV

I'm a beginner in Android Programming.I'm building an Android App for Image Steganography.I already developed this application for Desktop using Java and OpenCV,which works Perfectly.I'm trying to re-use the code,which I have.
I created a Mat Object and passed it to the Java Class(which I already have).I'm able to read the Pixel Intensities in the Mat Object,But,when I'm trying to modify those values (by using put()),my app is getting crashed.
The Exception thrown was
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.hari.imagesteganography, PID: 8850
java.lang.UnsupportedOperationException: Provided data element number (3) should be multiple of the Mat channels count (4)
at org.opencv.core.Mat.put(Mat.java:954)
at com.example.hari.imagesteganography.LSBImageStego.encodeOriginalMessageBinaryLength(LSBImageStego.java:101)
at com.example.hari.imagesteganography.LSBImageStego.encodeImage(LSBImageStego.java:239)
at com.example.hari.imagesteganography.EncodeActivity$1.onClick(EncodeActivity.java:98)
at android.view.View.performClick(View.java:6302)
at android.view.View$PerformClick.run(View.java:24782)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6518)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
The same method (put()) works perfectly fine running on Desktop.
Following is my Code in the Android application.
private void encodeOriginalMessageBinaryLength(){
// LOGGING THIS,SHOWS THAT THE FUNCTION EXECUTION STARTED
Log.i("URI" , "encodeOriginalMessageBinaryLength()");
String binary_of_originalMessageBinaryLength = Integer.toBinaryString(this.originalMessageBinaryLength);
if(binary_of_originalMessageBinaryLength.length()%2 !=0){
binary_of_originalMessageBinaryLength = "0" + binary_of_originalMessageBinaryLength;
}
int remaining = binary_of_originalMessageBinaryLength.length();
String newLsbBits;
for(int col = this.coverImage_cols -1 ; col >=0 ; col--){
if(remaining > 0){
newLsbBits = binary_of_originalMessageBinaryLength.substring( remaining - NUMBER_OF_BITS_REPLACING , remaining );
remaining -= NUMBER_OF_BITS_REPLACING;
}else{
newLsbBits = String.join("", Collections.nCopies(NUMBER_OF_BITS_REPLACING, "0"));
}
Log.i("URI","newLsbBits"+newLsbBits);
String modifiedBinaryString = this.getBeautifiedBinaryString(this.coverImage.get( this.coverImage_rows -1,col )[0]).substring(0 , this.getBeautifiedBinaryString(this.coverImage.get( this.coverImage_rows -1,col )[0]).length() - NUMBER_OF_BITS_REPLACING) + newLsbBits;
Log.i("URI","modifiedBianryString"+modifiedBinaryString);
double[] data = new double[3];
data[0] = Integer.parseInt(modifiedBinaryString , 2);
data[1] = this.coverImage.get( this.coverImage_rows -1,col )[1];
data[2] = this.coverImage.get( this.coverImage_rows -1,col )[2];
this.coverImage_rows -1,col )[0]);
// LOGGING THE DIMENSIONS OF THE IMAGE CORRECTLY,SHOWS THAT THE Mat Object
// IS INITIALIZED CORRECTLY
Log.i("URI" , new Integer(this.coverImage_rows).toString());
Log.i("URI" , new Integer(this.coverImage_cols).toString());
// EXECUTING BELOW LINE CAUSES THE APP TO CRASH
this.coverImage.put( this.coverImage_rows -1,col , data);
// BELOW MESSAGE IS NOT BEING LOGGED
Log.i("URI" , "encodeOriginalMessageBinaryLength() END");
}
}
The Corresponding Logcat log is as follows.
2019-02-08 12:30:37.019 1230-1230/com.example.hari.imagesteganography I/URI: encodeOriginalMessageBinaryLength()
2019-02-08 12:30:37.019 1230-1230/com.example.hari.imagesteganography I/URI: newLsbBits10
2019-02-08 12:30:37.019 1230-1230/com.example.hari.imagesteganography I/URI: modifiedBianryString10010110
2019-02-08 12:30:37.019 1230-1230/com.example.hari.imagesteganography I/URI: 1440
2019-02-08 12:30:37.019 1230-1230/com.example.hari.imagesteganography I/URI: 2560
The Mat Object and other attributes are being set as follows
public LSBImageStego(Mat coverImage){
this.coverImage = coverImage;
this.coverImage_rows = (int)coverImage.size().height;
this.coverImage_cols = (int)coverImage.size().width;
}
Is my code wrong?How to change Pixel Intensity values of Mat Object using OpenCV and Java in Android?
I believe that,this question is not duplicate of Using get() and put() to access pixel values in OpenCV for Java
OR
opencv java modify pixel values
,as they are not intended for Android, and the put() method described in those questions,works fine even for me on Desktop.
Method put() is overloaded in Mat class.
For each Mat type a specific data type is required.
For example, if you Mat type is CV_32S the data required is int [].
You can check the implementation of Mat class for all Mat types.

Template matching false positive

I am using template matching on openCV java to identify if a subimage exist in larger image. I want to get coordinates of match only if exact subimage is available in larger image. I am using this code but getting a lot of false positive. Attached is the subimage and larger image. Subimage is not present in larger image but i am getting match at (873,715) larger image subimage
public void run(String inFile, String templateFile, String outFile,
int match_method) {
System.out.println("Running Template Matching");
Mat img = Imgcodecs.imread(inFile);
Mat templ = Imgcodecs.imread(templateFile);
// / Create the result matrix
int result_cols = img.cols() - templ.cols() + 1;
int result_rows = img.rows() - templ.rows() + 1;
Mat result = new Mat(result_rows, result_cols, CvType.CV_32FC1);
// / Do the Matching and Normalize
Imgproc.matchTemplate(img, templ, result, match_method);
// Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new
// Mat());
Imgproc.threshold(result, result, 0.1, 1.0, Imgproc.THRESH_TOZERO);
// / Localizing the best match with minMaxLoc
MinMaxLocResult mmr = Core.minMaxLoc(result);
Point matchLoc;
if (match_method == Imgproc.TM_SQDIFF
|| match_method == Imgproc.TM_SQDIFF_NORMED) {
matchLoc = mmr.minLoc;
} else {
matchLoc = mmr.maxLoc;
}
double threashhold = 1.0;
if (mmr.maxVal > threashhold) {
System.out.println(matchLoc.x+" "+matchLoc.y);
Imgproc.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(),
matchLoc.y + templ.rows()), new Scalar(0, 255, 0));
}
// Save the visualized detection.
Imgcodecs.imwrite(outFile, img);
}
Here is an answer of the same question: Determine if an image exists within a larger image, and if so, find it, using Python
You will have to convert the python code to Java
I am not familiar with OpenCV in Java but OpenCV C++.
I don't think following code is necessary.
Imgproc.threshold(result, result, 0.1, 1.0, Imgproc.THRESH_TOZERO);
The min/max values of 'Mat result' will be between -1 and 1 if you uses normalized option. Therefore, your following code will not work because your threshold is 1.0 if you use normalized option.
if (mmr.maxVal > threshold)
Also, if you use CV_TM_SQDIFF, above code should be
if (mmr.minVal < threshold)
with proper threshold.
How about drawing minMaxLoc before comparing minVal/maxVal with threshold? to see it gives correct result? because match at (873,715) is ridiculous.

OpenCV 2.4 Bilateral filtering

In OpenCV android, is it possible to apply bilateral filtering? I know that I can do gaussian blurring like Imgproc.GaussianBlur(gray, gray, new Size(15,15), 0); but I cannot seem to find the code for bilateral filtering.
Seems it's possible like:
Imgproc.bilateralFilter(mat, dstMat, 10, 50, 0);
from here and here.
Update
This:
E/AndroidRuntime: FATAL EXCEPTION: Thread-1376 Process: PID: 30368 CvException [org.opencv.core.CvException: cv::Exception: /Volumes/build-storage/build/2_4_pack-android/opencv/modules‌​/imgproc/src/smooth.‌​cpp:1925: error: (-215) (src.type() == CV_8UC1 || src.type() == CV_8UC3) && src.type() == dst.type() && src.size() == dst.size() && src.data != dst.data in function void cv::bilateralFilter_8u(const cv::Mat&, cv::Mat&, int, double, double, int)
is because wrong color format of processing Mat. You should convert 4 channels RGBA format to 3 channels RGB for bilateralFilter() apply (like in bilateralFilterTutorial() method here). So, You code should be like that:
// load Mat from somewhere (e.g. from assets)
mSourceImageMat = Utils.loadResource(this, R.drawable.<your_image>);
// convert 4 channel Mat to 3 channel Mat
Imgproc.cvtColor(mSourceImageMat, mSourceImageMat, Imgproc.COLOR_BGRA2BGR);
// create dest Mat
Mat dstMat = mSourceImageMat.clone();
// apply bilateral filter
Imgproc.bilateralFilter(mSourceImageMat, dstMat, 10, 250, 50);
// convert to 4 channels Mat back
Imgproc.cvtColor(dstMat, dstMat, Imgproc.COLOR_RGB2RGBA);
// create result bitmap and convert Mat to it
Bitmap bm = Bitmap.createBitmap(mSourceImageMat.cols(), mSourceImageMat.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(dstMat, bm);
// show bitmap on ImageView
mImageView.setImageBitmap(bm);
The problem could be, that you use PNG image, which has a 4th channel for transparency. Convert it from 4 channel to 3 channel, before use.
Imgproc.cvtColor(src,dst,Imgproc.COLOR_BGRA2BGR);

OpenCV 3.0 svm always predicts 0 (java)

I'm new to opencv's svm's. I tried a sample classifier but it only returns 0 as the predicted label. I even used the value 5 for training as well as the prediction.
I've been changing the values for about a hundred times but i just don't get what's wrong. I'm using OpenCV 3.0 with Java. Here's my code:
Mat labels = new Mat(new Size(1,4),CvType.CV_32SC1);
labels.put(0, 0, 1);
labels.put(1, 0, 1);
labels.put(2, 0, 1);
labels.put(3, 0, 0);
Mat data = new Mat(new Size(1,4),CvType.CV_32FC1);
data.put(0, 0, 5);
data.put(1, 0, 2);
data.put(2, 0, 3);
data.put(3, 0, 8);
Mat testSamples = new Mat(new Size(1,1),CvType.CV_32FC1);
testSamples.put(0,0,5);
SVM svm = SVM.create();
TermCriteria criteria = new TermCriteria(TermCriteria.EPS + TermCriteria.MAX_ITER,100,0.1);
svm.setKernel(SVM.LINEAR);
svm.setType(SVM.C_SVC);
svm.setGamma(0.5);
svm.setNu(0.5);
svm.setC(1);
svm.setTermCriteria(criteria);
//data is N x 64 trained data Mat , labels is N x 1 label Mat with integer values;
svm.train(data, Ml.ROW_SAMPLE, labels);
Mat results = new Mat();
int predictedClass = (int) svm.predict(testSamples, results, 0);
Even if i change the lables to 1 and 2, I still get 0.0 as a response. So something has to be absolutely wrong... I just don't know what to do. Please help! :)
I had a similar problem in C++. I'm not too sure if it's the same in Java but in C++ the predictions were saved in the results Matrix instead of returned as a float.

OpenCV Java inpaint image format requirement

Been trying to get inpainting to work on Android,
int height = (int) viewMat.size().height;
int width = (int) viewMat.size().width;
Mat maskMat = new Mat();
maskMat.create(viewMat.size(), CvType.CV_8U);
maskMat.setTo(bColor);
Point r1 = new Point(width/2-width/10, height/2-height/10);
Point r2 = new Point(width/2+width/10, height/2+height/10);
Scalar color = new Scalar(1);
Core.rectangle(maskMat, r1, r2, color, Core.FILLED);
outMat.create(viewMat.size(), CvType.CV_8UC3);
viewMat.convertTo(outMat, CvType.CV_8UC3);
Photo.inpaint(outMat, maskMat, outMat, 1, Photo.INPAINT_TELEA);
Was greeted with,
Caused by: CvException [org.opencv.core.CvException: /home/reports/ci/slave_desktop/50-SDK/opencv/modules/photo/src/inpaint.cpp:744:
error: (-210) Only 8-bit 1-channel and 3-channel input/output images are supported in function void cvInpaint(const CvArr*, const CvArr*, CvArr*, double, int)
in logcat.
Been trying for hours creating Mats in various ways but to no valid.
CV_8U = 8 bit per channel, 1 channel. Right?
CV_8UC3 = 8 bit per channel, 3 channels. Right?
So what am I missing? I'm totally stumped.
...
Point r2 = new Point(width/2+width/10, height/2+height/10);
Scalar color = new Scalar(1);
Core.rectangle(maskMat, r1, r2, color, Core.FILLED);
Imgproc.cvtColor(viewMat, outMat, Imgproc.COLOR_RGBA2RGB);
Photo.inpaint(outMat, maskMat, outMat, 1, Photo.INPAINT_TELEA);
...
Turned out it was simply a matter of getting rid of the alpha channel via color conversion.
Image Inpaint Using OpenCv Android Studio
ImgMat = Mat()
Maskmat = Mat()
destmat=Mat()
Utils.bitmapToMat(BitmapImage, ImgMat)
Utils.bitmapToMat(BitmapMask, Maskmat)
Imgproc.cvtColor(ImgMat, ImgMat, Imgproc.COLOR_RGB2XYZ)
Imgproc.cvtColor(Maskmat, Maskmat, Imgproc.COLOR_BGR2GRAY)
Photo.inpaint(ImgMat,Maskmat,destmat, 15.0, INPAINT_NS)
InpaintBitmap= Bitmap.createBitmap(BitmapImage.getWidth(),
BitmapImage.getHeight(),
Bitmap.Config.ARGB_8888)
Imgproc.cvtColor(destmat, destmat, Imgproc.COLOR_XYZ2RGB);
Utils.matToBitmap(destmat, InpaintBitmap)
Description:
InpaintBitmap is an Inpaint color Bitmap
src image is a 3 channel image but mask bitmap is a 1 channel image

Categories

Resources