Android OpenCV color segmentation display on ImageView - android

I have searched everywhere for a solution but I haven't found any. I know this is a recurrent question, so I'll try to be precise.
I want to perform color segmentation in HSV using Android and OpenCV. I have an array of HSV samples corresponding to the color I want to segment, from which I obtain mean and std in order to obtain a range of values like:
lowerBoundH = new Scalar(meanSamples[0]-stdSamples[0]);
lowerBoundS = new Scalar(meanSamples[1]-stdSamples[1]);
upperBoundH = new Scalar(meanSamples[0]+stdSamples[0]);
upperBoundS = new Scalar(meanSamples[1]+stdSamples[1]);
Once I obtain these ranges for H, S and V I do the following (I only work with channels H & S):
mH = new Mat();
mS = new Mat();
List<Mat> channels = new ArrayList<Mat>(2);
// convert inputFrame to HSV for segmentation
Mat hsvFrame = new Mat();
convertToHSV(inputFrame, hsvFrame);
Core.split(hsvFrame, channels);
// get H&S channels of the HSVimage
mH = channels.get(0);
mS = channels.get(1);
if(mH!=null & mS!=null){
mHinRange = new Mat();
mSinRange = new Mat();
Core.inRange(mH, lowerBoundH, upperBoundH, mHinRange);
Core.inRange(mS, lowerBoundS, upperBoundS, mSinRange);
Mat filtered = new Mat(hsvFrame.height(), hsvFrame.width(), CvType.CV_8UC1, new Scalar(4));
Core.bitwise_and(mHinRange, mSinRange, filtered);
Mat aux = new Mat();
Mat mRgb = new Mat();
inputFrame.copyTo(mRgb);
Imgproc.cvtColor(filtered, mRgb, Imgproc.COLOR_GRAY2BGR, 0);
Imgproc.cvtColor(mRgb, aux, Imgproc.COLOR_BGR2RGBA, 0);
return aux;
}else{
Log.v("NULL","H OR S CHANNELS ARE NULL");
return null;
}
Then, I convert that segmented Mat to Bitmap in order to display it in an ImageView like so:
Bitmap bmp = null;
Mat tmp = new Mat(result.height(),result.width(), CvType.CV_8UC1, new Scalar(4));
try {
bmp = Bitmap.createBitmap(tmp.cols(), tmp.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(tmp, bmp);
}catch (CvException e){Log.d("Exception",e.getMessage());}
qrImage.setImageBitmap(bmp);
But when I display the resulting image I get a completely black image. I know this is wrong because the camera is facing directly to the colored object I want to segment.
What am I doing wrong?

What a stupid mistake. I was creating the Bitmap from the empty tmp Mat.
Instead, I need to create it using the result Mat:
Bitmap bmp = null;
try {
bmp = Bitmap.createBitmap(result.cols(), result.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(result, bmp);
}catch (CvException e){Log.d("Exception",e.getMessage());}
qrImage.setImageBitmap(bmp);

Related

Opencv Android : how to remove black background from thresholding output?

I am using OpenCV android library thresholding method for image segmentation, but the problem is that the output bitmap contains black background which I do not want please note that original image does not have any black background it is actually white. I am attaching the code for your reference, I am new to opencv and don't have much understanding about it also so kindly help me out.
private void Segmentation() {
Mat srcMat = new Mat();
gray = new Mat();
Utils.bitmapToMat(imageBmp, srcMat);
Imgproc.cvtColor(srcMat, gray, Imgproc.COLOR_RGBA2GRAY);
grayBmp = Bitmap.createBitmap(imageBmp.getWidth(), imageBmp.getHeight(), Bitmap.Config.RGB_565);
Utils.matToBitmap(gray, grayBmp);
grayscaleHistogram();
Mat threshold = new Mat();
Imgproc.threshold(gray, threshold, 0, 255, Imgproc.THRESH_BINARY_INV + Imgproc.THRESH_OTSU);
thresBmp = Bitmap.createBitmap(imageBmp.getWidth(), imageBmp.getHeight(), Bitmap.Config.RGB_565);
Utils.matToBitmap(threshold, thresBmp);
Mat closing = new Mat();
Mat kernel = Mat.ones(5, 5, CvType.CV_8U);
Imgproc.morphologyEx(threshold, closing, Imgproc.MORPH_CLOSE, kernel, new Point(-1, -1), 3);
closingBmp = Bitmap.createBitmap(imageBmp.getWidth(), imageBmp.getHeight(), Bitmap.Config.RGB_565);
Utils.matToBitmap(closing, closingBmp);
result = new Mat();
Core.subtract(closing, gray, result);
Core.subtract(closing, result, result);
resultBmp = Bitmap.createBitmap(imageBmp.getWidth(), imageBmp.getHeight(), Bitmap.Config.RGB_565);
Utils.matToBitmap(result, resultBmp);
Glide.with(ResultActivity.this).asBitmap().load(resultBmp).into(ivAfter);
}
enter image description here
What exactly do you want it to be then? Binary thresholding works like this:
if value < threshold:
value = 0
else:
value = 1
Of course you can convert it to a grayscale / RGB image and adjust the background to your liking. You can also invert your image (white background, black segmentation) by using the ~ operator.
segmented_image = ~ segmented_image
Edit: OpenCV has a dedicated flag to invert the results: CV_THRESH_BINARY_INV You are already using it, maybe try changing it to CV_THRESH_BINARY

finding edges and drawing contours with OpenCV on Android

I want to find the edges of image that I have used in basic image processing methods(blurring,sharpening..etc).I don't get any errors, but it isn't working.The program stops when it comes to on the Imgproc.Canny or Imgproc.findContours method with Debugging.
As a last resort, I wrote native code to find the edges, but I could not run it.
That is my code to call finding contours and execute it:
{
Mat rawImageMatGray = new Mat();
Imgproc.cvtColor(this.rawImageMat, rawImageMatGray, Imgproc.COLOR_BGR2GRAY);
Imgproc.adaptiveThreshold(rawImageMatGray, rawImageMatGray, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 55, 15);
Bitmap segmentImage = Bitmap.createBitmap(rawImageMatGray.cols(), rawImageMatGray.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(rawImageMatGray, segmentImage);
segmentImage = Contours(segmentImage);
return segmentImage;
}
private Bitmap Contours(Bitmap cannyImage) {
Mat cannyImageMat = new Mat();
Mat cannyImageMat2 = new Mat();
Utils.bitmapToMat(cannyImage, cannyImageMat);
Mat cannyEdges = new Mat();
Mat hierarchy = new Mat();
List<MatOfPoint> contourList = new ArrayList<MatOfPoint>();
//A list to store all the contours
//Converting the image to grayscale
//Imgproc.cvtColor(originalMat,grayMat,Imgproc.COLOR_BGR2GRAY);
Imgproc.Canny(cannyImageMat, cannyEdges, 80, 100);
//finding contours
Imgproc.findContours(cannyEdges, contourList, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
//Drawing contours on a new image
Mat contours = new Mat();
contours.create(cannyEdges.rows(), cannyEdges.cols(), CvType.CV_8UC3);
Random r = new Random();
for (int i = 0; i < contourList.size(); i++) {
Imgproc.drawContours(contours, contourList, i, new Scalar(r.nextInt(255), r.nextInt(255), r.nextInt(255)), -1);
}
//Converting Mat back to Bitmap
Bitmap cannyImage2 = Bitmap.createBitmap(cannyImageMat2.cols(),cannyImageMat2.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(cannyImageMat2, cannyImage);
Utils.matToBitmap(contours, cannyImage2);
return cannyImage2;
}

trouble using Perspective Transformation opencv

I'm working on Android and OpenCV 3.2, I want to apply the perspective transform but I have some trouble
I referenced to this official document OpenCV (go to perspective transform example ) http://docs.opencv.org/trunk/da/d6e/tutorial_py_geometric_transformations.html
but i get a rotated image result 90° or 180°
intput img = [input img ][2] output img output image 90 ° output 2 [output image 180 °][4]
this is my code
public void prespective(Bitmap img){
Mat imgSrc = new Mat();
Bitmap bmp32 = img.copy(Bitmap.Config.ARGB_8888, true);
Utils.bitmapToMat(bmp32, imgSrc);
Mat gray = new Mat();
cvtColor( imgSrc, gray, COLOR_RGBA2GRAY ); //Convert to gray
Mat thr = new Mat();
threshold( gray, thr, 125, 255, THRESH_BINARY ); //Threshold the gray
List<MatOfPoint> contours = new LinkedList<>(); // Vector for storing contours
findContours( thr, contours,new Mat(), RETR_CCOMP, CHAIN_APPROX_SIMPLE ); // Find the contours in the image
MatOfPoint m = biggestCountousMatOfPoint(contours);
RotatedRect box = Imgproc.minAreaRect(new MatOfPoint2f(m.toArray()));
Rect destImageRect = box.boundingRect();
Mat destImage = new Mat(destImageRect.size(),imgSrc.type());
final Point[] pts = new Point[4];
box.points(pts);
Mat _src = new MatOfPoint2f(pts[0], pts[1], pts[2], pts[3]);
Mat _dst = new MatOfPoint2f(new Point(0, 0), new Point(destImage.width() - 1, 0), new Point(destImage.width() - 1, destImage.height() - 1), new Point(0, destImage.height() - 1));
Mat perspectiveTransform=Imgproc.getPerspectiveTransform(_src, _dst);
Imgproc.warpPerspective(imgSrc, destImage, perspectiveTransform, destImage.size());
if(destImage.height() > 0 && destImage.width() > 0) {
Bitmap cinBitmapRotated = Bitmap.createBitmap(destImage.width(), destImage.height(), Bitmap.Config.ARGB_8888);
if (cinBitmapRotated != null) {
Utils.matToBitmap(destImage, cinBitmapRotated);
prenom2Cin.setImageBitmap(cinBitmapRotated);
}
}
}
what's the problem in my code ?

Grabcut OpenCV Android Error in Output Image

I am using OpenCV with Android and performing Grabcut on an image for foreground extraction, I am getting the output as expected but the output image has a blue tone and not as the original image that was provided? What is the solution to the problem?
I am attaching the code that I am using currently for the same.
private Mat performGrabCut(Mat image)
{
Mat firstMask = new Mat();
Mat foregroundModel = new Mat();
Mat backgroundModel = new Mat();
Mat mask;
Mat source = new Mat(1, 1, CvType.CV_8U, new Scalar(3.0));
Mat destination = new Mat();
Rect rect = new Rect(topLeft, bottomRight);
Imgproc.grabCut(image, firstMask, rect, backgroundModel, foregroundModel, 1, 0);
Core.compare(firstMask, source, firstMask, Core.CMP_EQ);
Mat foreground = new Mat(image.size(), CvType.CV_8UC3, new Scalar(255,255,255));
image.copyTo(foreground, firstMask);
return foreground;
}

rectangle region segmentation

I'm trying to get the red rectangle region below "C", as image below:
And below is my source code use Opencv4Android:
public void threshold() {
Mat rgbMat = new Mat();
Mat grayMat = new Mat();
Mat edgeMat = new Mat();
Utils.bitmapToMat(bmp, rgbMat);
Mat intermediate = new Mat();
Imgproc.cvtColor(rgbMat, intermediate, Imgproc.COLOR_BGR2GRAY);
Imgproc.GaussianBlur(intermediate, intermediate, new Size(3, 3), 0);
Imgproc.threshold(intermediate, intermediate, 190, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
Imgproc.Canny(intermediate, intermediate, 60, 140);
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat mHierarchy = new Mat();
Imgproc.findContours(intermediate, contours, mHierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
Scalar CONTOUR_COLOR = new Scalar(255,0,0,255);
Log.e(TAG, "Contours count: " + contours.size());
Imgproc.drawContours(intermediate, contours, -1, CONTOUR_COLOR);
Bitmap edgeBmp = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Config.ARGB_8888);
Utils.matToBitmap(intermediate, edgeBmp);
imageView.setImageBitmap(edgeBmp);
}
but the result is not as I expected: as image below:
As log show, Contours count: 372, and the rectangle region is discontinuous, How can I get the contour of the red rectangle region, and filter another useless region. I have referenced some other questions, but the question still not be solved, Could you do me a favor?
[update] change the code by the suggest from Morotspaj,
public void thresholdNew() {
Mat rgbMat = new Mat();
Mat grayMat = new Mat();
Utils.bitmapToMat(bmp, rgbMat);
Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_BGR2GRAY);
Vector<Mat> bgr_planes = new Vector<Mat>();
Core.split(rgbMat, bgr_planes);
Mat redMat = bgr_planes.get(2);
Mat redness = new Mat();
Core.subtract(redMat, grayMat, redness);
Mat intermediateMat1 = new Mat();
Mat intermediateMat2 = new Mat();
Imgproc.GaussianBlur(redness, intermediateMat1, new Size(15,15), 0);
Imgproc.GaussianBlur(redness, intermediateMat2, new Size(55,55), 0);
Mat red_mask = new Mat();
Core.subtract(intermediateMat1, intermediateMat2, red_mask );
Imgproc.threshold(red_mask , red_mask , 90, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
Mat masked_image = rgbMat.clone();
masked_image = masked_image.setTo(new Scalar(255,0,0), red_mask );
Bitmap edgeBmp = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Config.ARGB_8888);
Utils.matToBitmap(masked_image, edgeBmp);
imageView.setImageBitmap(edgeBmp);
}
But the result is not as I expected and different with Morotspaj's.
Any error exist in the above code?
[update] Sorry, I am very busy these days, I will be try again later, and If I can not implement with Java, I will use Morotspaj's code through JNI. I will be update soon.
I made a filter to mask out the red rectangle region, just for you ;)
Mat rgbMat = imread("red_rectangle.jpg", -1);
Mat grayMat;
cvtColor(rgbMat, grayMat, COLOR_BGR2GRAY);
// Separate the red channel and compare it to the gray image
Mat channels[3];
split(rgbMat, channels);
Mat redness = Mat_<float>(channels[2]) - Mat_<float>(grayMat);
// Find the sharp red region
Mat red_blur1;
Mat red_blur2;
GaussianBlur(redness, red_blur1, Size(15,15), 0);
GaussianBlur(redness, red_blur2, Size(55,55), 0);
Mat red_mask = (red_blur1-red_blur2) > 2;
// Store result
Mat masked_image = rgbMat.clone();
masked_image.setTo(Scalar(0,0,255), red_mask);
imwrite("red_mask.png", red_mask);
imwrite("masked_image.png", masked_image);
The GaussianBlur method calls can be replaced by boxFilter if you need better performance, and the constants here and there can of course be tweaked. Hope this helps!
EDIT: Taking the difference of two differently blurred images is known as Difference of Gaussians (DoG). It finds changes in a certain scale depending on the size of the kernels. The smaller kernel is used to smooth away small details and noise. The bigger kernel destroys the details we are interested in but not the regions with very smooth changes that we don't want. By taking the difference between them we end up with only the details in the scale we are interested in! A mask can then be created easily by thresholding with the > operator.

Categories

Resources