Imgproc.drawContours(....) not showing up - java

In my android app I get an image from the gallery as a bitmap with something like this
Bitmap bitm = getMyImage("Thanks!");
and I have a Mat called mat declared like this:
Mat mat = new Mat(bitm.getHeight(), bitm.getWidth(), CVType.CV_8UC3);
I'm trying to get contour areas from the image, which I've successfully gotten then draw it back on the original image with:
Imgproc.drawContours(mat, contours, -1, new Scalar(200,200,0), 2);
displayMat(mat);
If I use it like that, it works but the contours are drawn on a blank image which isn't what I want. I want it to be drawn on the original image. If I use
Utils.bitmapToMat(bitm, mat);
before the previous snippet of code, the displayed image is just the preprossed image without the 'Drawn' contours. Why?

Asfaik Android uses images with alpha values, so CV_8UC4 is the right data type.
So
Mat mat = new Mat(bitm.getHeight(), bitm.getWidth(), CVType.CV_8UC3);
Imgproc.drawContours(mat, contours, -1, new Scalar(200,200,0), 2);
displayMat(mat);
draws the contours correctly on an empty/blank 8UC3 image (if memory empty).
But if you want to draw on the input image by first converting Utils.bitmapToMat(bitm, mat); you'll overwrite your 8UC3 memory and replace it by 8UC4 data. After that you draw Scalar(200,200,0) which will use a 4th channel, but cv::Scalar automatically adds those channels with default zero values, so you draw your contours in transparent. So use Scalar(200,200,0,255) instead and it should give your expected results.
Mat mat = new Mat(bitm.getHeight(), bitm.getWidth(), CVType.CV_8UC3);
Utils.bitmapToMat(bitm, mat);
Imgproc.drawContours(mat, contours, -1, new Scalar(200,200,0,255), 2);
displayMat(mat);
The other method would be to convert the bitmap to 8UC3, but I'm not sure how to do that.

Related

Join different sized images with ROI

At the first contact with Java OpenCV (3.3.1, windows 8 x64) I'm trying to join two different size images with ROI dynamically. Here a bit of my code:
Mat _mat = Utils.imageFileToMat(new File("angelina_jolie.jpg")); //Angelina's face
Mat grayMat = new Mat();
Imgproc.cvtColor(_mat, grayMat, Imgproc.COLOR_BGR2GRAY);
Rect rect = new Rect(new Point(168, 104), new Point(254, 190)); //Angelina's eye ROI
Mat truncated = _mat.submat(rect); //Angelina's eye mat
Mat merge = _mat.clone();
truncated.copyTo(merge);
//show _mat
//show truncated
//show merge
What I want to see is Angelina Jolie with her eye on grayscale.
What I see is assertions or the truncated image only (just the eye).
I tried with copyTo(mat, mask), setOf, and a lot of things but always get a new assertion.
Should I change the size of truncated to the size of mat to match sizes? how can I do that programmatically?
Mat::copyTo documentation:
The method copies the matrix data to another matrix. Before copying
the data, the method invokes :
m.create(this->size(),this->type());
so that the destination matrix is reallocated
if needed. While m.copyTo(m); works flawlessly, the function does not
handle the case of a partial overlap between the source and the
destination matrices.
When the operation mask is specified, if the Mat::create call shown
above reallocates the matrix, the newly allocated matrix is
initialized with all zeros before copying the data.
#param m
Destination matrix. If it does not have a proper size or type before
the operation, it is reallocated.
Since you're your src and dst images don't have the same size and channels, the destination image is reallocated and initialized with zeros. To avoid that make sure both images have same dimensions and number of channels.
Imgproc.cvtColor(grayMat, grayMat, Imgproc.COLOR_GRAY2BGR);
Now create a mask:
Mat mask = new Mat(_mat.size(), CvType.CV_8UC1, new Scalar(0));
Imgproc.rectangle(mask, new Point(168, 104), new Point(254, 190),new Scalar(255));
// copy gray to _mat based on mask
Mat merge = _mat.clone();
grayMat.copyTo(merge,mask);

color detection using opencv

I am trying to extract the title bar from the image below using opencv.
I am using the following code -
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat original = Imgcodecs.imread("D:/test_bg_extract.png", Imgcodecs.CV_LOAD_IMAGE_UNCHANGED);
Mat hsvMat = new Mat();
Imgproc.cvtColor(original, hsvMat, Imgproc.COLOR_BGR2HSV);
Mat dst = new Mat();
Core.inRange(hsvMat, new Scalar(23,231,205), new Scalar(23,231,205), dst);
I used a color picker to determine the HSV value of the title bar (from the hsvMat converted to display image). When the run the code to display the output, I see a blank screen. I can't tell what I am doing wrong. Am I picking the wrong HSV color? If I know the exact RGB code then should I even be converting image to HSV? (this however seems to be the standard method from many code examples on the internet).
Ok, I solved it using RGB based color, switching to BGR while specifying the color that I need. So this worked -
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat original = Imgcodecs.imread("D:/test_bg_extract.png", Imgcodecs.CV_LOAD_IMAGE_UNCHANGED);
Mat dst = new Mat();
Core.inRange(original, new Scalar(132,178,205), new Scalar(132,178,205), dst);

OpenCV finds only image border as contour

I want to get all the outer contours with RETR_EXTERNAL but for some weird reason openCV thinks that the image border is a contour too and therefore discards all inner contours. What exactly am I doing wrong here?
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(imageA, contours, hierarchy, Imgproc.RETR_EXTERNAL,
Imgproc.CHAIN_APPROX_SIMPLE);
for (int i = 0; i < contours.size(); i++) {
double[] c = hierarchy.get(0, i);
Rect rect = Imgproc.boundingRect(contours.get(i));
Core.rectangle(image, new Point(rect.x, rect.y),
new Point(rect.x + rect.width, rect.y + rect.height),
new Scalar(0, 255, 0), 3);
}
Input (imageA was processed to this before contour-finding):
Output:
EDIT:
Problem partially solved
Inverting the pixels so that black is the background and white the foreground helped with the image above image. However I still get inner contours on some images. Like this one:
Input
Output
Your input image isnt good enought o extract the contours you want to have.
Your input contours are these (part of your image):
each color is a single contour (and some of the white ones)
For the red contour I've drawn the bounding rectangle which is the same method that you used to display the contours. All the other colored contours aren't inside of the red contour, but just inside of the bounding rectangle, that's why they are found even though you selected to only find the outer contours.
What you really want is something like this:
but to get that result, your input image must have that lines of the ellipse connected, too!!
For your input image it will be very hard to extract those lines, without getting lines of the ground too, but an easy approach could be to use a couple of dilation operations followed by the same number of erosion operations on your input image, before extracting contours. This won't be stable for all setting though ;)

Copying a small image into the camera frame with OpenCV for Android

I'm currently working on a program that should draw a small image onto the camera frame. With Android OpenCV, you do have the following function to process a frame:
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
Mat rgba = inputFrame.rgba();
mDetector.setFrame(rgba);
mDetector.processFrame();
return rgba;
}
Where the Mat rgba then gets displayed on the screen. My Detector should now process the frame rgba (change it).
Here is the relevant code:
public void processFrame() {
// (1) Doesn't work
Rect roi = new Rect(0, 0, 100, 100);
Mat submat = mOutputFrame.submat(roi);
Mat image = new Mat(100, 100, CvType.CV_8UC3, new Scalar(0,0,0));
image.copyTo(submat);
// (2) Does work
// --- mComparatorImage is the same size as mOutputFrame.
// --- mComparatorImage is 8bit greyscale, mOutputFrame is the rgba CameraFrame
mComparatorImage = mComparatorHolder.getCurrentImage();
mComparatorImage.copyTo(mOutputFrame);
// (3) Should work (but doesn't)
Imgproc.resize(mComparatorImage, mResizedImageClone, new Size (200, 100));
Mat bSubmat = mOutputFrame.submat(new Rect(0, 0, 200, 100));
mResizedImageClone.copyTo(bSubmat);
}
What I'm trying to do is to copy a resized version of mComparatorImage into the camera frame that is referenced by mOutputFrame (mOutputFrame = rgba).
So I tried doing (3).
FYI: mResizedImageClone is of type Mat and is initialized as a new Mat()
Doing (3) doesn't change the mOutputFrame.
(2) Then I tried copying the whole mComparatorImage (type Mat and same size as mOutputFrame) to mOutputFrame. This worked suprisingly.
(1) Then I thought the problem has to be something with submat, because copying the big image works, but copying a small version of it into mOutputFrame doesnt. So I tried copying a little black image into mOutputFrame. This doesn't work either, although I followed other answers here.
What could be the problem? There is no error, but the camera frame stays the same in (1) and (3)
If you need any additional info, let me know.
Isa
Okay, I've found it, it was a little bit tricky.
The copyTo function using submatrices works only properly if the src and the dest Mat are of the same type. Otherwise, it just does ... nothing. (It rather should complain!)
Instead of using rect, I used submat with parameters (row_start, row_end, col_start, col_end)
Also be aware that the dimensions of the submat (#cols and #rows) have to exactly match the src image that is used in copyTo.
So here is my solution for (1):
// (1) Inserting a little black rect into the camera frame:
Mat submat = mOutputFrame.submat(0, 100, 0, 100);
Mat image = new Mat(100, 100, mOutputFrame.type(), new Scalar(0,0,0));
image.copyTo(submat);
And my solution for (3):
// (3) Resizing and inserting an arbitrary grey image into the rgba camera frame
Imgproc.resize(mComparatorImage, mResizedImageClone, new Size (200, 100));
Imgproc.cvtColor(mResizedImageClone, mResizedImageClone, Imgproc.COLOR_GRAY2RGBA);
Mat submat = mOutputFrame.submat(0, 100, 0, 200);
mResizedImageClone.copyTo(submat);

OpenCV: Dominoes circular spots/disks detection

I am developing an Android app that calculates the sum of all points of the being-seen dominoes pieces -shown in picture- using OpenCV for Android.
The problem is, I can't find a way to filtering other contours and counting only dots I see in the dominoes, I tried to use Canny edge finding then use HoughCircles, but with no result, as I don't have an absolute top view of the rocks and HoughCircles detect perfect circles only :)
Here is my code:
public Mat onCameraFrame(Mat inputFrame) {
inputFrame.copyTo(mRgba);
Mat grey = new Mat();
// Make it greyscale
Imgproc.cvtColor(mRgba, grey, Imgproc.COLOR_RGBA2GRAY);
// init contours arraylist
List<MatOfPoint> contours = new ArrayList<MatOfPoint>(200);
//blur
Imgproc.GaussianBlur(grey, grey, new Size(9,9), 10);
Imgproc.threshold(grey, grey, 80, 150, Imgproc.THRESH_BINARY);
// because findContours modifies the image I back it up
Mat greyCopy = new Mat();
grey.copyTo(greyCopy);
Imgproc.findContours(greyCopy, contours, new Mat(), Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_NONE);
// Now I have my controus pefectly
MatOfPoint2f mMOP2f1 = new MatOfPoint2f();
//a list for only selected contours
List<MatOfPoint> SelectedContours = new ArrayList<MatOfPoint>(400);
for(int i=0;i<contours.size();i++)
{
if(here I should put a condition that distinguishes my spots, eg: if contour inside is black and is a black disk)
{
SelectedContours.add(contours.get(i));
}
}
Imgproc.drawContours(mRgba, SelectedContours, -1, new Scalar(255,0,0,255), 1);
return mRgba;
}
EDIT:
One unique feature of my contours after threshold is they're totally black from inside, is there anyway I could calculate the mean color/intensity for a given contour ?
There is a similiar problem and possible solution on SO, titled Detection of coins (and fit ellipses) on an image. Here you will find some recomendations about opencv's function fitEllipse.
You should take a look at this for more info on opencv's function fitEllipse.
Also, to detect only black elements in an image, you can use HSV color model, to find only black colors. You can find an explanation here.

Categories