I'm using OpenCV in Android Studio.
I have an image with a white background and a black area. In the black area, I have some contours. I want to detect the contours which are inside of the black area like this:
But my code is not working; instead it returns the sides of the entire image as contours:
What am I doing wrong?
public Mat[] Smudge_detection(Mat color_mat)
{
Mat lab_mat=new Mat();
Imgproc.cvtColor(color_mat, lab_mat, Imgproc.COLOR_RGB2Lab);
List<Mat> lab_list = new ArrayList(3);
Core.split(lab_mat, lab_list);
Mat eqHist=new Mat();
Imgproc.equalizeHist(lab_list.get(0),eqHist);
lab_list.set(0,eqHist);
Core.merge(lab_list,lab_mat);
Mat rgb_mat=new Mat();
Mat gray_mat=new Mat();
Imgproc.cvtColor(lab_mat,rgb_mat,Imgproc.COLOR_Lab2RGB);
Imgproc.cvtColor(rgb_mat,gray_mat,Imgproc.COLOR_RGB2GRAY);
MatOfInt histSize = new MatOfInt(256);
MatOfInt channels=new MatOfInt(0);
Mat hist_ = new Mat();
MatOfFloat histRange = new MatOfFloat(0, 220);
List<Mat>gray_lst= new ArrayList<Mat>(1);
Core.split(gray_mat,gray_lst);
Imgproc.calcHist(gray_lst,channels,new Mat(),hist_,histSize,histRange,false);
Core.MinMaxLocResult mml= Core.minMaxLoc(hist_);
double tresh=mml.maxLoc.y;
// for remove white area around of black circle
Mat thresh_mat=new Mat();
Imgproc.threshold(gray_mat,thresh_mat,tresh,220,THRESH_BINARY);
Mat notmat=new Mat();
Core.bitwise_not(thresh_mat,notmat);
Mat morph_mat=new Mat();
Imgproc.morphologyEx(notmat,morph_mat,Imgproc.MORPH_CLOSE, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(2,2)));
List<MatOfPoint> contours=new ArrayList<>();
final Mat hierarchy = new Mat();
Imgproc.findContours(morph_mat,contours,hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
Mat drawmat=color_mat.clone();
Imgproc.drawContours(drawmat,contours,0,new Scalar(255,50,180),15);
Mat[] out=new Mat[2];
out[0]=contours.get(0);
out[1]=drawmat.clone();
return out;
}
My result in python, you may take for reference:
The library is closing, i'll go...
Here is my python code:
#!/usr/bin/python3
# 2017.10.08 21:48:13 CST
import cv2
import numpy as np
img = cv2.imread("t.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
retval, threshed = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
_,contours,h = cv2.findContours(threshed, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
dst1 = img.copy()
dst2 = img.copy()
cv2.drawContours(dst1, contours, -1, (0,255,0), 3)
cnts = []
for cnt in contours:
rect = cv2.boundingRect(cnt)
x,y,w,h = rect
if w < 10 or h < 10 >>w > 100 or h > 100:
continue
cnts.append(cnt)
print(len(cnts))
cv2.drawContours(dst2, cnts, -1, (0,255,0), 3)
res = np.hstack((dst1, dst2))
cv2.imwrite("res.png", res)
cv2.imshow("res", res)
cv2.waitKey()
cv2.destroyAllWindows()
Related
I have been trying to remove the black background from the grabcut output using python opencv.
import numpy as np
import cv2
img = cv2.imread(r'myfile_1.png')
mask = np.zeros(img.shape[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
rect = (1,1,665,344)
cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img = img*mask2[:,:,np.newaxis]
cv2.imshow('img',img)
cv2.imwrite('img.png',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Above code I had written to save the grabcut output. Please suggest, How I can remove the black background and make it transparent?
I have achieved this by using the following snippet.
import cv2
file_name = "grab.png"
src = cv2.imread(file_name, 1)
tmp = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
_,alpha = cv2.threshold(tmp,0,255,cv2.THRESH_BINARY)
b, g, r = cv2.split(src)
rgba = [b,g,r, alpha]
dst = cv2.merge(rgba,4)
cv2.imwrite("test.png", dst)
This is java code. After use grabcut, the result background is transparent.
public Bitmap removeBackground(Bitmap bitmap) {
//GrabCut part
Mat img = new Mat();
Utils.bitmapToMat(bitmap, img);
int r = img.rows();
int c = img.cols();
Point p1 = new Point(c / 100, r / 100);
Point p2 = new Point(c - c / 100, r - r / 100);
Rect rect = new Rect(p1, p2);
Mat mask = new Mat();
Mat fgdModel = new Mat();
Mat bgdModel = new Mat();
Mat imgC3 = new Mat();
Imgproc.cvtColor(img, imgC3, Imgproc.COLOR_RGBA2RGB);
Imgproc.grabCut(imgC3, mask, rect, bgdModel, fgdModel, 5, Imgproc.
GC_INIT_WITH_RECT);
Mat source = new Mat(1, 1, CvType.CV_8U, new Scalar(3.0));
Core.compare(mask, source/* GC_PR_FGD */, mask, Core.CMP_EQ);
//This is important. You must use Scalar(255,255, 255,255), not Scalar(255,255,255)
Mat foreground = new Mat(img.size(), CvType.CV_8UC3, new Scalar(255,
255, 255,255));
img.copyTo(foreground, mask);
// convert matrix to output bitmap
bitmap = Bitmap.createBitmap((int) foreground.size().width,
(int) foreground.size().height,
Bitmap.Config.ARGB_8888);
Utils.matToBitmap(foreground, bitmap);
return bitmap;
}
I'm trying to get this rectangle from that image:
Found this solution using OpenCV:
private Bitmap findRectangle(Bitmap src) throws Exception {
Mat imageMat = new Mat();
Utils.bitmapToMat(src, imageMat);
Mat imgSource=imageMat.clone();
Imgproc.cvtColor(imgSource, imageMat, Imgproc.COLOR_BGR2GRAY);
//find the contours
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Imgproc.findContours(imageMat, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_NONE);
Imgproc.Canny(imageMat,imageMat,0,255);
Bitmap canny=Bitmap.createBitmap(imageMat.cols(),imageMat.rows(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(imageMat,canny);
Imgproc.GaussianBlur(imageMat, imageMat, new org.opencv.core.Size(1, 1), 2, 2);
Bitmap blur=Bitmap.createBitmap(imageMat.cols(),imageMat.rows(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(imageMat,blur);
MatOfPoint temp_contour = contours.get(0); //the largest is at the index 0 for starting point
for (int idx = 0; idx < contours.size(); idx++) {
temp_contour = contours.get(idx);
//check if this contour is a square
MatOfPoint2f new_mat = new MatOfPoint2f( temp_contour.toArray() );
int contourSize = (int)temp_contour.total();
MatOfPoint2f approxCurve_temp = new MatOfPoint2f();
Imgproc.approxPolyDP(new_mat, approxCurve_temp, contourSize*0.05, true);
if (approxCurve_temp.total() == 4) {
MatOfPoint points = new MatOfPoint( approxCurve_temp.toArray() );
Rect rect = Imgproc.boundingRect(points);
Imgproc.rectangle(imgSource, new Point(rect.x,rect.y), new Point(rect.x+rect.width,rect.y+rect.height), new Scalar(255, 0, 0, 255), 3);
}
}
Bitmap analyzed=Bitmap.createBitmap(imgSource.cols(),imgSource.rows(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(imgSource,analyzed);
return analyzed;
}
The best i got was this:
The problem is that the rectangle isn't perfect, maybe find the white numbers inside of that can be a best option, but i don't know too much of OpenCV.
Original image:
This is a very simple C++ implementation which tries to search for the text box. The accuracy of the detection depends on three parameters:
The thresh value provided to cv::threshold function to convert gray image to binary.
The height/width ratio, since the height of the text box is relatively smaller than the width, and the area of the text box.
Mat img = imread("image.jpg",-1), gray, binary;
/*pre-processing steps*/
uchar thresh = 80;
cvtColor(img, gray, cv::COLOR_BGR2GRAY);
GaussianBlur(gray, gray, Size(7,7), 0);
// change the thresh value to fine tune this program for your images
threshold(gray, binary, thresh, 255, cv::THRESH_BINARY_INV);
/*contour searching*/
std::vector<std::vector<Point>> contours;
std::vector<Vec4i> hierarchy;
findContours(binary, contours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);
/*Filtering contours based on height/width ratio and bounding box area*/
std::vector<Rect> boxes;
double box_ratio = 0.3;
int box_area = 20000;
for(auto& cnt : contours)
{
auto box = minAreaRect(cnt).boundingRect();
// we are searching for a rectangle which a has relatively large area,
// and the height is smaller than the width, so the
// height/width ratio should be small. Change the these two values for fine tuning
if((min(box.width,box.height)/double(max(box.width,box.height)) < box_ratio) && box.area() > box_area )
{
boxes.push_back(box);
}
}
Mat txt_box = img(boxes.at(0));
Here is the almost same solution on java:
private Bitmap findRoi(Bitmap sourceBitmap) {
Mat sourceMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CV_8UC3);
Utils.bitmapToMat(sourceBitmap, sourceMat);
Mat grayMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CV_8UC3);
Imgproc.cvtColor(sourceMat, grayMat, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold(grayMat, grayMat, 125, 200, Imgproc.THRESH_BINARY);
// find contours
List<MatOfPoint> whiteContours = new ArrayList<>();
Rect largestRect = null;
Imgproc.findContours(grayMat, whiteContours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
// find appropriate bounding rectangles
for (MatOfPoint contour : whiteContours) {
RotatedRect boundingRect = Imgproc.minAreaRect(new MatOfPoint2f(contour.toArray()));
double rectangleArea = boundingRect.size.area();
// test min ROI area in pixels
if (rectangleArea > 10000) {
Point rotated_rect_points[] = new Point[4];
boundingRect.points(rotated_rect_points);
Rect rect = Imgproc.boundingRect(new MatOfPoint(rotated_rect_points));
// test horizontal ROI orientation and aspect ratio
if (rect.width > 3 * rect.height) {
if (largestRect == null) {
largestRect = rect;
} else {
if (rect.width > largestRect.width) {
largestRect = rect;
}
}
}
}
}
Mat roiMat = new Mat(sourceMat, largestRect);
Bitmap bitmap = Bitmap.createBitmap(roiMat.cols(), roiMat.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(roiMat, bitmap);
return bitmap;
}
Also, you can use additional information: red number places on the right.
I am working on a program that extracts the stickers on the puzzle and then later on finds the RGB of them. Currently, I am at the point where I want to remove any contours that aren't "square" like. I was wondering how I could do this.
What I do is I load the image, gray it, blur it, canny edge detection, dilate it find contours and draw them.
Is there a way I can draw around the contours instead of filling them in? And remove contours that aren't roughly the same size around or have almost 90 degree angles?
public static void main(String[] args) {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat capturedFrame = Imgcodecs.imread("first.png");
//Gray
Mat gray = new Mat();
Imgproc.cvtColor(capturedFrame, gray, Imgproc.COLOR_BGR2GRAY);
//Blur
Mat blur = new Mat();
Imgproc.blur(gray, blur, new Size(3,3));
//Canny image
Mat canny = new Mat();
Imgproc.Canny(blur, canny, 20, 40, 3, true);
Imgcodecs.imwrite("test.png", canny);
//System.exit(0);
Mat kernel = Imgproc.getStructuringElement(1, new Size(3,3));
Mat dilated = new Mat();
Imgproc.dilate(canny,dilated, kernel);
List<MatOfPoint> contours = new ArrayList<>();
//find contours
Imgproc.findContours(dilated, contours, new Mat(), Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_NONE);
//draw contours
Imgproc.cvtColor(capturedFrame, capturedFrame, Imgproc.COLOR_BGR2RGB);
for(int i = 0; i < contours.size(); i++){
Imgproc.drawContours(capturedFrame, contours, i, new Scalar(0, 0, 255), -1);
}
Imgcodecs.imwrite("after.png", capturedFrame);
Imshow img = new Imshow("firstImg");
img.show(capturedFrame);
}
Here is the initial image:
Here is the image with the contours drawn:
To draw non filled contours use non negative thickness: Imgproc.drawContours(capturedFrame, contours, i, new Scalar(0, 0, 255), 1); for instance.
To remove unnecessary find contour area, and just skip too large or too small of them when drawing.
I making a function that extract the RGB Channels from an image to calculate the average color of a picture.
I want the result like this:
Image A : Red layer: 25%, Green layer: 40%, Blue layer: 35%.
Im using Opencv in Android, and have a function that can split the layers, but, how to calculate???
Mat mRgb = m;
List<Mat> lRgb = new ArrayList<Mat>(3);
List<Mat> red = new ArrayList<Mat>(3);
List<Mat> green = new ArrayList<Mat>(3);
List<Mat> blue = new ArrayList<Mat>(3);
Mat mRed = new Mat();
Mat mGreen = new Mat();
Mat mBlue = new Mat();
//Split the RGB channels
Core.split(mRgb, lRgb);
//Apply Mat
Mat mR = lRgb.get(0);
Mat mG = Mat.zeros(m.rows(), m.cols(), CvType.CV_8UC1);
Mat mB = Mat.zeros(m.rows(), m.cols(), CvType.CV_8UC1);
red.add(mR);
red.add(mG);
red.add(mB);
Core.merge(red, mRed); // Return return red matrix
//Apply Mat
mR = Mat.zeros(m.rows(), m.cols(), CvType.CV_8UC1);
mG = lRgb.get(1);
mB = Mat.zeros(m.rows(), m.cols(), CvType.CV_8UC1);
green.add(mR);
green.add(mG);
green.add(mB);
Core.merge(green, mGreen); //Return green matrix
//Apply Mat
mR = Mat.zeros(m.rows(), m.cols(), CvType.CV_8UC1);
mG = Mat.zeros(m.rows(), m.cols(), CvType.CV_8UC1);
mB = lRgb.get(2);
blue.add(mR);
blue.add(mG);
blue.add(mB);
Core.merge(blue, mBlue); //Return blue matrix
Can you help me with these?
I am trying to extract text from image(binarize) using opencv javacpp-presets (version 3.0.0-1.0) and using the code-snippit below. The snippit is a translate from this python version of the code.
The input image is from file and is loaded through imread but the code fails at findContours with the following error message:
OpenCV Error: Bad flag (parameter or structure field) (Unrecognized or unsupported array type) in cvGetMat, file src\array.cpp, line 2494
Suggested solutions from here don't work for me. Any help greatly appreciated!
// Load the image
Mat image_orig = imread(inputFile);
if ( image_orig.empty() ) { LOGGER.error("Empty image!");}
this.image = new Mat();
//Add a border to the image for processing sake
copyMakeBorder(image_orig, this.image, 50, 50, 50, 50, BORDER_CONSTANT);
//# Calculate the width and height of the image
this.img_y = this.image.arrayHeight();
this.img_x = this.image.arrayWidth();
if (DEBUG)
LOGGER.info("Image is " + this.img_x + "x" + this.img_x);
//Split out each channel
Mat red = new Mat();
Mat green = new Mat();
Mat blue = new Mat();
MatVector v = new MatVector(blue, green, red);
split(image, v);
//Run canny edge detection on each channel
Mat blue_edges = new Mat();
Canny(blue, blue_edges, 200, 250);
Mat green_edges = new Mat();
Canny(green, green_edges, 200, 250);
Mat red_edges = new Mat();
Canny(red, red_edges, 200, 250);
//Join edges back into image
Mat edges = new Mat();
MatVector vEdges = new MatVector(red_edges, green_edges, blue_edges);
merge(vEdges, edges);
//Find the contours
Mat edgesCopy = new Mat();
edges.copyTo(edgesCopy);
Mat hierarchy = new Mat();
MatVector contours = new MatVector();
findContours(edgesCopy, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);
Gosh, silly me. I should have used the input from the MatVector i.s.o. the empty Mat's for the Canny detection. Then using the grayscale as #user3510227 suggests i get the following working code:
// Load the image
Mat image_orig = imread(inputFile);
if ( image_orig.empty() ) { LOGGER.error("Empty image!");}
this.image = new Mat();
//Add a border to the image for processing sake
copyMakeBorder(image_orig, this.image, 50, 50, 50, 50, BORDER_CONSTANT);
//# Calculate the width and height of the image
this.img_y = this.image.arrayHeight();
this.img_x = this.image.arrayWidth();
if (DEBUG)
LOGGER.info("Image is " + this.img_x + "x" + this.img_x);
//Split out each channel
Mat red = new Mat();
Mat green = new Mat();
Mat blue = new Mat();
MatVector v = new MatVector(red, green, blue);
split(image, v);
//Run canny edge detection on each channel
Mat blue_edges = new Mat();
Canny(v.get(0), blue_edges, 200, 250);
Mat green_edges = new Mat();
Canny(v.get(1), green_edges, 200, 250);
Mat red_edges = new Mat();
Canny(v.get(2), red_edges, 200, 250);
//Join edges back into image
Mat edges = new Mat();
MatVector vEdges = new MatVector(red_edges, green_edges, blue_edges);
merge(vEdges, edges);
LOGGER.info("Type: " + edges.type());
//convert to grayscale
Mat gray = new Mat();
cvtColor(edges, gray, COLOR_BGR2GRAY);
Mat blur = new Mat();
GaussianBlur(gray, blur, new Size(5,5), 0);
Mat edgesCopy = new Mat();
adaptiveThreshold(blur, edgesCopy, 255,1,1,11,2);
//Find the contours
Mat hierarchy = new Mat();
MatVector contours = new MatVector();
findContours(edgesCopy, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);