I'm currently working on an application that will split a scanned image (that contains multiple receipts) into individual receipt images.
Below is the sample image:
sample image
I was able to detect the edges of each receipts in the scanned image using canny function of OpenCV.
Below is the sample image with detected edges:
sample image with detected edges
... and the sample code is
Mat src = Highgui.imread(filename);
Mat gray = new Mat();
int threshold = 12;
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
Imgproc.blur(gray, gray, new Size(3, 3));
Imgproc.Canny(gray, gray, threshold, threshold * 3, 3, true);
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
Imgproc.findContours(gray, contours, hierarchy,
Imgproc.RETR_CCOMP,
Imgproc.CHAIN_APPROX_SIMPLE);
if (hierarchy.size().height > 0 && hierarchy.size().width > 0) {
for (int idx = 0; idx >= 0; idx = (int) hierarchy.get(0, idx)[0]) {
Rect rect = Imgproc.boundingRect(contours.get(idx));
Core.rectangle(src, new Point(rect.x, rect.y),
new Point(rect.x + rect.width, rect.y + rect.height),
new Scalar(255, 0, 0));
}
}
Now my problem is, I don't know how am I going to identify the 3rd receipt since unlike with the first 2 it is not enclosed in one rectangular shape which I will use as the basis for splitting the image.
I've heard that for me to extract the 3rd image, I must use a clustering algorithm like DBSCAN, unfortunately I can't find one.
Anyone knows how am I going to identify the 3rd image?
Thank you in advance!
Related
I'm trying to develop simple application (OpenCv, Tesseract and Java) where i need to get numbers from a photo of water meter. I am newbie to OpenCV and i am stuck on detection of numbers in rectangles.
So i want to achieve "00295" value as result.
Here is a example of water meter
But i am not able to achieve this result.
Steps:
Apply Gray filter
GaussianBlur filter 3x3
Sobel filter Threshold
And doing OCR with number characters allowed only
But in result i get bunch of random numbers from other labels.
Can you please give some suggestions and show way how to detect this 5 rectangles and get digits from them ?
Thanks in advance.
Here is code:
private static final int
CV_THRESH_OTSU = 8;
public static void main(String[] args) {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat img = new Mat();
Mat imgGray = new Mat();
Mat imgGaussianBlur = new Mat();
Mat imgSobel = new Mat();
Mat imgThreshold = new Mat();
//Path to picture
String inputFilePath = "D:/OCR/test.jpg";
img = Imgcodecs.imread(inputFilePath);
Imgcodecs.imwrite("preprocess/1_True_Image.png", img);
Imgproc.cvtColor(img, imgGray, Imgproc.COLOR_BGR2GRAY);
Imgcodecs.imwrite("preprocess/2_imgGray.png", imgGray);
Imgproc.GaussianBlur(imgGray,imgGaussianBlur, new Size(3, 3),0);
Imgcodecs.imwrite("preprocess/3_imgGaussianBlur.png", imgGray);
Imgproc.Sobel(imgGaussianBlur, imgSobel, -1, 1, 0);
Imgcodecs.imwrite("preprocess/4_imgSobel.png", imgSobel);
Imgproc.threshold(imgSobel, imgThreshold, 0, 255, CV_THRESH_OTSU);
Imgcodecs.imwrite("preprocess/5_imgThreshold.png", imgThreshold);
File imageFile = new File("preprocess/5_imgThreshold.png");
Tesseract tesseract = new Tesseract();
//tessdata directory
tesseract.setDatapath("tessdata");
tesseract.setTessVariable("tessedit_char_whitelist", "0123456789");
try {
String result = tesseract.doOCR(imageFile);
System.out.println(result);
} catch (TesseractException e) {
System.err.println(e.getMessage());
}
}
}
If the position of the water filter won't change from image to image, could you just manually crop the image to your desired size? Also, after you blur the image, try using an adaptive threshold followed by canny edge detection. As a result, your image will only have the hard edges present. Then you could find contours on the image and filter through those contours till they fit the desired size that you want.
I am detecting a rectangle and comparing the color to a urine test strip.
How can i detect all of squares? I want to detect the remaining squares in the picture below. I have tried changing the brightness and contrast
Here is my code:
MainActivity.java
...
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
...
Bitmap img = BitmapFactory.decodeStream(in);
in.close();
Bitmap changeImg = changeBitmapContrastBrightness(img, (float)1, 10);
Mat cMap = new Mat();
Utils.bitmapToMat(changeImg, cMap);
List<MatOfPoint> squres = processImage(cMap);
for (int i = 0; i < squres.size(); i++) {
setLabel(cMap, String.valueOf(i), squres.get(i));
}
Bitmap resultBitmap = Bitmap.createBitmap(cMap.cols(), cMap.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(cMap, resultBitmap);
imgView.setImageBitmap(resultBitmap);
...
}
...
private static List<MatOfPoint> processImage(Mat img){
ArrayList<MatOfPoint> squares = new ArrayList<>();
Mat matGray = new Mat();
Mat matCny = new Mat();
Mat matBlur = new Mat();
Mat matThresh = new Mat();
Mat close = new Mat();
// 노이즈 제거위해 다운스케일 후 업스케일
// Imgproc.pyrDown(matInit, matBase, matBase.size());
// Imgproc.pyrUp(matBase, matInit, matInit.size());
// GrayScale
Imgproc.cvtColor(img, matGray, Imgproc.COLOR_BGR2GRAY);
// Blur
Imgproc.medianBlur(matGray, matBlur, 5);
// // Canny Edge 검출
// Imgproc.Canny(matBlur, matCny, 0, 255);
// // Binary
Imgproc.threshold(matBlur, matThresh, 160, 255, Imgproc.THRESH_BINARY_INV);
Imgproc.morphologyEx(matThresh, close, Imgproc.MORPH_CLOSE, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3,3)));
// // 노이즈 제거
// Imgproc.erode(matCny, matCny, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new org.opencv.core.Size(6, 6)));
// Imgproc.dilate(matCny, matCny, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new org.opencv.core.Size(12, 12)));
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
Imgproc.findContours(close, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
double min_area = 0;
double max_area = 10000;
for(MatOfPoint cnt : contours){
double contourArea = Imgproc.contourArea(cnt);
if(contourArea > min_area && contourArea < max_area){
squares.add(cnt);
}
}
return squares;
}
App result Image
Original Image
Please help me..
Your code is correctly identifying the smaller boxes and ignoring the very large box which is the strip, so the basics are all in place.
It is not recognising the smaller boxes on the strip - given that your contour finding is clearly working this suggests that the threshold value in your threshold function (160 in your code above) may need to be adjusted so it includes the color boxes on the strip which do not have a black contour. The black contour will be definitely detached.
Whatever the root causes you'll probably find the easiest way to debug it is to output and look at the intermediate images generated - this will allow you check visually yourself very quickly the result of your blurring and thresholding.
You could also take a look at using adaptive thresholding if you are working with multiple images and find the threshold is not something you can reliably determine in advance. The documentation is here: https://docs.opencv.org/2.4/modules/imgproc/doc/miscellaneous_transformations.html?highlight=adaptivethreshold and there is a very nice example in this answer here: https://stackoverflow.com/a/31290735/334402
adaptiveThreshold parameters allow you fine tune its behaviour and it is worth experimenting with them see what works best for a given type if image:
I am new to OpenCV and tried to use ColorBlobDetector from the samples of OpenCv, but it returns error in
Core.multiply(contour, new Scalar(4,4), contour);
saying "Fatal signal 11 (SIGSEGV), code 1, fault addr 9x9 in tid 10111 (Thread-28303)
I am using eclipse.
I have search for whole day already, can anybody tell me why this error happen and how to fix it? I didn't edit anything in the code.
And I would like to know how can I modify the code so that I can specify the app to detect white contour only from the start of the app?
Thank you.
i would suggest just a temporary solution ( i can't try whole code because i don't use java )
you can edit as following ( this change probably decrease process speed but it should work)
public void process(Mat rgbaImage) {
//Imgproc.pyrDown(rgbaImage, mPyrDownMat);
//Imgproc.pyrDown(mPyrDownMat, mPyrDownMat);
Imgproc.cvtColor(rgbaImage, 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);
}
}
}
I am new to Opencv. I am using it in java. Could you please tell me where I went wrong in the following coding? I always get the unsupported format error - "Unsupported format or combination of formats ([Start]FindContours supports only CV_8UC1 images when mode != CV_RETR_FLOODFILL otherwise supports CV_32SC1 images only) in cvStartFindContours".
Thanks in advance.
Mat dst = new Mat();
destination.convertTo(dst,CvType.CV_32SC1);
List<MatOfPoint> points = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(dst, points, hierarchy, Imgproc.RETR_FLOODFILL, Imgproc.CHAIN_APPROX_SIMPLE);
Mat cImg = new Mat(dst.size(), dst.type());
for (int i = 0; i < points.size(); i++) {
Imgproc.drawContours(cImg, points, i, new Scalar(0, 255,0), -1);
}
Imgcodecs.imwrite("contour.jpg", cImg);
This is the part of code which is not working good. I know this question has been asked earlier, but i have done everything i could do but still couldn't figure out the reason. This is the portion of code
Mat imgSource = original_image.clone();
// apply gaussian blur to smoothen lines of dots
Log.i(TAG, "Blur");
Imgproc.GaussianBlur(imgSource, imgSource, new Size(5, 5), 5);
// convert the image to black and white
Log.i(TAG, "B&W");
Log.i(TAG,Integer.toString(imgSource.type()));
Log.i(TAG,Integer.toString(imgSource.channels()));
Mat gray = new Mat(imgSource.size(),CvType.CV_8UC1);
Imgproc.cvtColor(imgSource, gray, Imgproc.COLOR_BGR2GRAY);
// convert the image to black and white does (8 bit)
Log.i(TAG, "Canny");
Imgproc.Canny(gray, gray, 50, 50);
// find the contours
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Log.i(TAG, "Contours");
Imgproc.findContours(gray, contours, new Mat(), Imgproc.RETR_LIST,
Imgproc.CHAIN_APPROX_SIMPLE);
Log.i(TAG,"Contours done");
//if no contours are detected
if(contours.size() == 0){
Log.i(TAG,"contour size is 0");
return gray;
}
In my Logcat i see the message "contour size is 0".
When i load the gray mat, i see a coloured image which explains why their were no contours detected.
Please suggest something.
Hello I think you may remove this row from your code:
Imgproc.Canny(gray, gray, 50, 50);