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);
Related
I'm trying to only detect, draw & fill/color the battery black while filling everything else white, but I'm getting other lines from the background as well. How do i go about fixing this?
Note: I cannot change the background of the input image.
Below is the output of an image i got after drawing the contours of everything that's black using the following code:
if(getIntent().hasExtra("byteArray")) {
b = BitmapFactory.decodeByteArray(getIntent().getByteArrayExtra("byteArray"),0,getIntent().getByteArrayExtra("byteArray").length);
contours = new ArrayList<>();
srcMat= new Mat();
gray = new Mat();
matHSV = new Mat();
Utils.bitmapToMat(b,srcMat);
Imgproc.cvtColor(srcMat, gray, Imgproc.COLOR_RGBA2GRAY);
Imgproc.Canny(gray, gray, 20, 20*3, 3, true);
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);
}
mask = new Mat(new Size(srcMat.cols(), srcMat.rows() ), CvType.CV_8UC1);
mask.setTo(new Scalar(255));
black = new Scalar(0,0,0);
Imgproc.drawContours(mask, contours, -1, black, 10);
for (MatOfPoint contour: contours) {
Imgproc.fillPoly(mask, Arrays.asList(contour), black);
}
Mat masked = new Mat();
srcMat.copyTo(masked, mask);
Utils.matToBitmap(mask, b);
imgR.setImageBitmap(b);
}
Output:
Input:
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'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?
Currently I'm developing an app that will detect colored circles. I'm trying to do this by following this tutorial, where guy detects red circles on image with Python. I've written the same code, just for Java.
Mat mat = new Mat(bitmap.getWidth(), bitmap.getHeight(),
CvType.CV_8UC3);
Mat hsv_image = new Mat();
Utils.bitmapToMat(bitmap, mat);
Imgproc.cvtColor(mat, hsv_image, Imgproc.COLOR_BGR2HSV);
Mat lower_red_hue_range = new Mat();
Mat upper_red_hue_range = new Mat();
Core.inRange(hsv_image, new Scalar(0, 100, 100), new Scalar(10, 255, 255), lower_red_hue_range);
Core.inRange(hsv_image, new Scalar(160, 100, 100), new Scalar(179, 255, 255), upper_red_hue_range);
Utils.matToBitmap(hsv_image, bitmap);
mutableBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
image.setImageBitmap(mutableBitmap);
Image I use is identical to one from tutorial:
This is image with applied BGR2HSV:
When I execute the code using lower red hue range, it detects the blue circle. When I use upper red hue range it gives me black bmp(doesn't detect anything). How can it be? What am I doing wrong? This is literally copy moved from python to Java. Why's the result different then?
Thanks in advance.
Your mat is of CvType.CV_8UC1 image, i.e. you are working on a grayscale image. Try with CvType.CV_8UC3
Mat mat = new Mat(bitmap.getWidth(), bitmap.getHeight(), CvType.CV_8UC3);
hsv_image should look like this:
How to select a custom range:
You may want to detect a green circle.
Well, in HSV, tipically the range is:
H in [0,360]
S,V in [0,100]
However, for CV_8UC3 images, each component H,S,V can be represented by only 256 values at most, since it's stored in 1 byte. So, in OpenCV, the ranges H,S,V for CV_8UC3 are:
H in [0,180] <- halved to fit in the range
S,V in [0,255] <- stretched to fit the range
So to switch from typical range to OpenCV range you need to:
opencv_H = typical_H / 2;
opencv_S = typical_S * 2.55;
opencv_V = typical_V * 2.55;
So, green colors are around the value of hue of 120. The hue can have a value in the interval [0,360].
However, for Mat3b HSV images, the range for H is in [0,180], i.e. is halved so it can fit in a 8 bit representation with at most 256 possible values.
So, you want the H value to be around 120 / 2 = 60, say from 50 to 70.
You also set a minimum value for S,V to 100 in order to prevent very dark (almost black) colors.
Mat green_hue_range
inRange(hsv_image, cv::Scalar(50, 100, 100), cv::Scalar(70, 255, 255), green_hue_range);
use the following code and pass color to Blob detector and then pass an image to the detector
private Scalar converScalarRgba2HSV(Scalar rgba) {
Mat pointMatHsv= new Mat();
Mat pointMatRgba = new Mat(1, 1, CvType.CV_8UC3, rgba);
Imgproc.cvtColor(pointMatRgba,pointMatHsv, Imgproc.COLOR_RGB2HSV_FULL, 4);
return new Scalar(pointMatHsv.get(0, 0));}
// Blob Detector
public class ColorBlobDetector {
// Lower and Upper bounds for range checking in HSV color space
private Scalar mLowerBound = new Scalar(0);
private Scalar mUpperBound = new Scalar(0);
// Minimum contour area in percent for contours filtering
private static double mMinContourArea = 0.1;
// Color radius for range checking in HSV color space
private Scalar mColorRadius = new Scalar(25,50,50,0);
private Mat mSpectrum = new Mat();
private List<MatOfPoint> mContours = new ArrayList<MatOfPoint>();
Mat mPyrDownMat = new Mat();
Mat mHsvMat = new Mat();
Mat mMask = new Mat();
Mat mDilatedMask = new Mat();
Mat mHierarchy = new Mat();
public void setColorRadius(Scalar radius) {
mColorRadius = radius;
}
public void setHsvColor(Scalar hsvColor) {
double minH = (hsvColor.val[0] >= mColorRadius.val[0]) ? hsvColor.val[0]-mColorRadius.val[0] : 0;
double maxH = (hsvColor.val[0]+mColorRadius.val[0] <= 255) ? hsvColor.val[0]+mColorRadius.val[0] : 255;
mLowerBound.val[0] = minH;
mUpperBound.val[0] = maxH;
mLowerBound.val[1] = hsvColor.val[1] - mColorRadius.val[1];
mUpperBound.val[1] = hsvColor.val[1] + mColorRadius.val[1];
mLowerBound.val[2] = hsvColor.val[2] - mColorRadius.val[2];
mUpperBound.val[2] = hsvColor.val[2] + mColorRadius.val[2];
mLowerBound.val[3] = 0;
mUpperBound.val[3] = 255;
Mat spectrumHsv = new Mat(1, (int)(maxH-minH), CvType.CV_8UC3);
for (int j = 0; j < maxH-minH; j++) {
byte[] tmp = {(byte)(minH+j), (byte)255, (byte)255};
spectrumHsv.put(0, j, tmp);
}
Imgproc.cvtColor(spectrumHsv, mSpectrum, Imgproc.COLOR_HSV2RGB_FULL, 4);
}
public Mat getSpectrum() {
return mSpectrum;
}
public void setMinContourArea(double area) {
mMinContourArea = area;
}
public void process(Mat rgbaImage) {
Imgproc.pyrDown(rgbaImage, mPyrDownMat);
Imgproc.pyrDown(mPyrDownMat, mPyrDownMat);
Imgproc.cvtColor(mPyrDownMat, mHsvMat, Imgproc.COLOR_RGB2HSV_FULL);
Core.inRange(mHsvMat, mLowerBound, mUpperBound, mMask);
Imgproc.dilate(mMask, mDilatedMask, new Mat());
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Imgproc.findContours(mDilatedMask, contours, mHierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
// Find max contour area
double maxArea = 0;
Iterator<MatOfPoint> each = contours.iterator();
while (each.hasNext()) {
MatOfPoint wrapper = each.next();
double area = Imgproc.contourArea(wrapper);
if (area > maxArea)
maxArea = area;
}
// Filter contours by area and resize to fit the original image size
mContours.clear();
each = contours.iterator();
while (each.hasNext()) {
MatOfPoint contour = each.next();
if (Imgproc.contourArea(contour) > mMinContourArea*maxArea) {
Core.multiply(contour, new Scalar(4,4), contour);
mContours.add(contour);
}
}
}
public List<MatOfPoint> getContours() {
return mContours;
}}
now set detector
public void initDetector() {
mDetector = new ColorBlobDetector();
mSpectrum = new Mat();
mBlobColorRgba = new Scalar(255);
mBlobColorHsv = new Scalar(255);
SPECTRUM_SIZE = new org.opencv.core.Size(500, 64);
CONTOUR_COLOR = new Scalar(0, 255, 0, 255);
mDetector.setHsvColor(converScalarRgba2HSV(new Scalar(0,255,255,255)));
Imgproc.resize(mDetector.getSpectrum(), mSpectrum, SPECTRUM_SIZE, 0, 0, Imgproc.INTER_LINEAR_EXACT);
mIsColorSelected = true;
}
now pass an image to a detector object
Mat mRgba = new Mat(inputFrame.height(), inputFrame.width(), CvType.CV_8UC4);
mRgba = inputFrame;
mDetector.process(mRgba);
List<MatOfPoint> contours = mDetector.getContours();
Log.e(TAG, "Contours count: " + contours.size());
drawContours(mRgba, contours, -1, CONTOUR_COLOR);
return mRgba;
Happy Codeing !!!
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.