I'm writing simple application on android using open cv that will draw red contours on black detected objects.
Here is the processing code
Mat hsv = new Mat();
Mat maskInrange = new Mat();
Mat dilateMat = new Mat();
List<MatOfPoint> contours= new ArrayList<>();
Imgproc.cvtColor(rgbaImage, hsv, Imgproc.COLOR_RGB2HSV);
Scalar lowerThreshold = new Scalar(0, 0, 0);
Scalar upperThreshold = new Scalar(15, 15, 15);
Core.inRange(hsv, lowerThreshold, upperThreshold, maskInrange);
Imgproc.dilate(maskInrange, dilateMat, new Mat());
Mat h=new Mat();
Imgproc.findContours(dilateMat, contours, h, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
Imgproc.drawContours ( rgbaImage, contours, -1, new Scalar(255,0,0), 1);
The problem is that instead of one big contour representing the object there are many little unstable contours.
I guess it's about noise, but what should be the next step in improving the app?
Related
How do i draw contours on only black object, and filling everything else in the background white?
My code currently is able to draw contours on an image:
Bitmap b = BitmapFactory.decodeByteArray(getIntent().getByteArrayExtra("byteArray"),0,getIntent().getByteArrayExtra("byteArray").length);
srcMat= new Mat();
Utils.bitmapToMat(b,srcMat);
Mat gray = new Mat();
Imgproc.cvtColor(srcMat, gray, Imgproc.COLOR_RGBA2GRAY);
Imgproc.Canny(gray, gray, 20, 20*3, 3, true);
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
Imgproc.findContours(gray,contours,hierarchy,Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
for (int contourIdx = 0; contourIdx < contours.size(); contourIdx++) {
Imgproc.drawContours(srcMat, contours, contourIdx, new Scalar(0, 0, 255), -1);
}
Utils.matToBitmap(gray, b);
imgR.setImageBitmap(b);
You should create and apply mask, like in answer to this question. You can do this, for example, by this way (insert code below after Your Imgproc.findContours() call instead of for (int contourIdx = ...:
// create Mat for mask
Mat mask = new Mat(new Size(srcMat.cols(), srcMat.rows() ), CvType.CV_8UC1);
mask.setTo(new Scalar(0.0));
// create Scalar for color of mask objects
Scalar white = new Scalar(255, 255, 255);
// draw contours border and fill them
Imgproc.drawContours(mask, contours, -1, white, 10);
for (MatOfPoint contour: contours) {
Imgproc.fillPoly(mask, Arrays.asList(contour), white);
}
// create mat foe masked image
Mat masked = new Mat();
// apply mask to srcMat and set result to masked
srcMat.copyTo(masked, mask);
Then change gray mat in Utils.matToBitmap() call to masked:
Utils.matToBitmap(masked, b);
imgR.setImageBitmap(b);
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;
}
I am trying to detect laser light dot of any colour of laser.and i have done some reference code from here OpenCV Android Track laser dot
That code is running perfectly for Only RED colour detection and i want any colour of laser dot detection.
I am new in OpenCV.
Here's what i have done till now :
Mat originalFrame= new Mat();
Mat frame = new Mat();
cvf.rgba().copyTo(originalFrame);
cvf.rgba().copyTo(frame);
Mat frameH;
Mat frameV;
Mat frameS;
mRgba = cvf.rgba();
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
// Mat frameS;
// Convert it to HSV
Imgproc.cvtColor(frame, frame, Imgproc.COLOR_RGB2HSV);
// Split the frame into individual components (separate images for H, S,
// and V)
mChannels.clear();
Core.split(frame, mChannels); // Split channels: 0-H, 1-S, 2-V
frameH = mChannels.get(0);
frameS = mChannels.get(1);
frameV = mChannels.get(2);
// Apply a threshold to each component
Imgproc.threshold(frameH, frameH, 155, 160, Imgproc.THRESH_BINARY);
// Imgproc.threshold(frameS, frameS, 0, 100, Imgproc.THRESH_BINARY);
Imgproc.threshold(frameV, frameV, 250, 256, Imgproc.THRESH_BINARY);
// Perform an AND operation
Core.bitwise_and(frameH, frameV, frame);
//
// Core.bitwise_and(frame,frameS,frame);
Imgproc.findContours(frame, contours, hierarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0, 0));
hierarchy.release();
for ( int contourIdx=0; contourIdx < contours.size(); contourIdx++ )
{
// Minimum size allowed for consideration
MatOfPoint2f approxCurve = new MatOfPoint2f();
MatOfPoint2f contour2f = new MatOfPoint2f( contours.get(contourIdx).toArray() );
//Processing on mMOP2f1 which is in type MatOfPoint2f
double approxDistance = Imgproc.arcLength(contour2f, true)*0.02;
Imgproc.approxPolyDP(contour2f, approxCurve, approxDistance, true);
//Convert back to MatOfPoint
MatOfPoint points = new MatOfPoint( approxCurve.toArray() );
// Get bounding rect of contour
Rect rect = Imgproc.boundingRect(points);
Imgproc.rectangle(originalFrame, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 0, 255), 3);
}
This is old question but i have findy my solution using core.InRange
Follow my alternative version
#Override
public void onCameraViewStarted(int width, int height) {
mat1 = new Mat(height, width, CvType.CV_16UC4);
mat2 = new Mat(height, width, CvType.CV_16UC4);
}
#Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
Mat src = inputFrame.rgba();
Imgproc.cvtColor(inputFrame.rgba(), mat1, Imgproc.COLOR_BGR2HSV);
//rangeLow and RangeHight is Scalar
Core.inRange(mat1, rangeLow, rangeHight, mat2);
Core.MinMaxLocResult mmG = Core.minMaxLoc(mat2);
Core.rotate(src, src, Core.ROTATE_90_CLOCKWISE);
Imgproc.circle(src,mmG.maxLoc,30,new Scalar(0,255,0), 5, Imgproc.LINE_AA);
return src;
}
The code you posted carries out two thresholding operations. One on the hue and one on the value. It then ANDs the results together. Because of the way it thresholds the hue, the effect is that it is looking for a bright red(ish) spot.
My first solution would be to look for just a bright spot (so just look on the hue frame). You might also try looking for high saturations (except that a laser spot may well overload the sensors, and result in an apparently unsaturated pixel).
To select the appropriate threshold values, you will have to experiment with various images.
I am new to OpenCV and trying to track a circular object using find contours on Android using 3.1.0. I am following the sample color blob detector to write the code, but the drawContours() function never draws anything when I run the application. Here is the onCameraFrame() function, which is doing all the processing. Can you tell me what I am doing wrong?
EDIT: New version of the code: Still does not work as expected. drawContours() draws around any black objects, and the function produces a very noisy result. Here is what it now looks like.
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
Mat rgba = inputFrame.rgba();
Mat grayMat = new Mat();
Imgproc.cvtColor(rgba, grayMat, Imgproc.COLOR_RGBA2GRAY);
Mat processedMat = new Mat();
Imgproc.threshold(grayMat, processedMat, 0, 255, Imgproc.THRESH_BINARY);
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
List<MatOfPoint> matchedContours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(processedMat, contours, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
for(MatOfPoint contour : contours){
double actualArea = Imgproc.contourArea(contour);
double width = contour.width();
double radius = width/2;
double calculatedArea = Math.PI * Math.pow(radius, 2);
if((actualArea - calculatedArea) < 10000) { //chose some arbitrary threshold because I am not sure what to make this
matchedContours.add(contour);
}
}
Imgproc.drawContours(rgba, matchedContours, -1, new Scalar(0,255,0));
return rgba;
}
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.