0

I have the following image:

enter image description here

I'm able to grayscale this image with a following OpenCV Java code:

public static Mat grayscale(Mat mat) throws IOException {
    Mat gray = new Mat(mat.size(), CvType.CV_8UC1);
    if (mat.channels() == 3) {
        Imgproc.cvtColor(mat, gray, Imgproc.COLOR_RGB2GRAY);
    } else if (mat.channels() == 1) {
        mat.copyTo(gray);
    } else {
        throw new IOException("Invalid image type:" + mat.type());
    }
    return gray;
}

Right now I need to remove background noise(scanning artifacts, lines) from this image and leave only the scanned card there. I think I have to use threshold, erode there but unable to do it with OpenCV.

I'm playing with it but the result is awful right now and destroys all of the image:

public static Mat clean(Mat srcImage) {
    Core.normalize(srcImage, srcImage, 0, 255, Core.NORM_MINMAX);
    Imgproc.threshold(srcImage, srcImage, 0, 255, Imgproc.THRESH_OTSU);
    //Imgproc.erode(srcImage, srcImage, new Mat());
    Imgproc.dilate(srcImage, srcImage, new Mat(), new Point(0, 0), 1);
    return srcImage;
}

Please show how it can be achieved with OpenCV Java.

UPDATED

I'm trying to port Python example provided by janu777 to Java. This is my current code:

Mat image = Imgcodecs.imread("test.png");

Mat gray = new Mat();
Imgproc.cvtColor(image, gray, Imgproc.COLOR_BGR2GRAY);
Core.absdiff(gray, new Scalar(255), gray);

double thresh = Imgproc.threshold(gray, gray, 5, 255, Imgproc.THRESH_TOZERO);

Mat kernel1 = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(11, 11));
Mat kernel2 = Mat.ones(3, 3, CvType.CV_8U);

Mat erosion = new Mat();
Imgproc.erode(gray, erosion, kernel2);
Mat dilation = new Mat();
Imgproc.dilate(erosion, dilation, kernel1);

Right now I have another result than was provide in the answer:

enter image description here

I'm unable to find a place where thresh parameter should be applied and also I'm not using the iterations parameter for Imgproc.erode and Imgproc.dilate methods because the method signature in this case also requires additional Point anchor parameter which I do not have right now.

What am I doing wrong ?

alexanoid
  • 24,051
  • 54
  • 210
  • 410

1 Answers1

3

Following @Silencer comment, I implemented the idea using python and Opencv. Please convert the algorithm to work with java.

Step 1: Morphological Operations using an elliptical structuring element.

img = cv2.imread("test.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.subtract(255,gray)
ret,thresh = cv2.threshold(gray,5,255,cv2.THRESH_TOZERO)
kernel1 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(11,11))
kernel2 = np.ones((3,3),np.uint8)
erosion = cv2.erode(thresh,kernel2,iterations = 1)
dilation = cv2.dilate(erosion,kernel1,iterations = 7)

Result: Morph-op

Step 2: Finding contours

im2,contours, hierarchy = cv2.findContours(dilation,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
c = max(contours, key = cv2.contourArea)
rect = cv2.minAreaRect(c)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(img,[box],0,(0,0,255),2)

Result:Bounding Rectangle

Step 3: Rotate image and crop

# rotate img
angle = rect[2]
rows,cols = img.shape[0], img.shape[1]
M = cv2.getRotationMatrix2D((cols/2,rows/2),angle,1)
img_rot = cv2.warpAffine(img,M,(cols,rows))

# rotate bounding box
rect0 = (rect[0], rect[1], 0.0)
box = cv2.boxPoints(rect)
pts = np.int0(cv2.transform(np.array([box]), M))[0]    
pts[pts < 0] = 0

# crop
img_crop = img_rot[pts[1][1]:pts[0][1], 
                   pts[1][0]:pts[2][0]]
cv2.imshow("finalresult.jpg",img_crop)

Rotate and crop

janu777
  • 1,940
  • 11
  • 26
  • Thanks! I'm trying to port your example to Java. I have updated my question with the current results. What am I doing wrong? – alexanoid Feb 06 '18 at 18:57
  • Can you check the subtract line. Because it should invert the Colors in your image. Why is the value 0.255? Post the result image after this step – janu777 Feb 07 '18 at 01:57
  • In case of `Core.subtract(gray, new Scalar(255), gray);` the image is completely black. – alexanoid Feb 07 '18 at 04:37
  • The idea of that line is to invert the pixels. You must find a way to do it. – janu777 Feb 07 '18 at 04:58
  • I guess you can use absdiff for this – janu777 Feb 07 '18 at 05:07
  • Thanks! Now, after the `Core.absdiff(gray, new Scalar(255), gray);` the gray matrix is inverted. Right now something is wrong with `Imgproc.erode()` because I don't know how and where to apply the `thresh` parameter in Java version of this method. – alexanoid Feb 07 '18 at 06:51
  • You are supposed to erode with kernel2 and dilate with kernel1. Check properly – janu777 Feb 07 '18 at 06:53
  • Unfortunatly I don't know right now how to translate the following line `erosion = cv2.erode(thresh,kernel1,iterations = 1)` to Java equivalent - `Imgproc.erode(..)` I do not see the place in the cImgproc.erode` where `thresh` parameter can be applied – alexanoid Feb 07 '18 at 06:57
  • sorry I made a small change in the code. You must erode with kernel2 and not kernel1 – janu777 Feb 07 '18 at 07:00
  • Thanks, please see my updated question. The result is pretty close but still not equal. – alexanoid Feb 07 '18 at 07:09
  • How to properly convert it ? – alexanoid Feb 07 '18 at 07:38
  • You must achieve the result by trial and error method. Just follow the python code for verification. I don't know java. Good Luck with your project :) – janu777 Feb 07 '18 at 07:40
  • Thanks for your help! – alexanoid Feb 07 '18 at 07:47