androidopencvthresholdblobs

Detect black ink blob on paper - Opencv Android


I'm new to openCV, I've been getting into the samples provided for Android.

My goals is to detect color-blobs so I started with color-blob-detection sample.

I'm converting color image to grayscale and then thresholding using a binary threshold.

The background is white, blobs are black. I want to detect those black blobs. Also, I would like to draw their contour in color but I'm not able to do it because image is black and white.

I've managed to accomplish this in grayscale but I don't prefer how the contours are drawn, it's like color tolerance is too high and the contour is bigger than the actual blob (maybe blobs are too small?). I guess this 'tolerance' I talk about has something to do with setHsvColor but I don't quite understand that method.

Thanks in advance! Best Regards

UPDATE MORE INFO

The image I want to track is of ink splits. Imagine a white piece of paper with black ink splits. Right now I'm doing it in real-time (camera view). The actual app would take a picture and analyse that picture.

As I said above, I took color-blob-detection sample (android) from openCV GitHub repo. And I add this code in the onCameraFrame method (in order to convert it to black and white in real-time) The convertion is made so I don't mind if ink is black, blue, red:

mRgba = inputFrame.rgba();
/**************************************************************************/
/** BLACK AND WHITE **/
// Convert to Grey
Imgproc.cvtColor(inputFrame.gray(), mRgba, Imgproc.COLOR_GRAY2RGBA, 4);

Mat blackAndWhiteMat = new Mat ( H, W, CvType.CV_8U, new Scalar(1));
double umbral = 100.0;
Imgproc.threshold(mRgba, blackAndWhiteMat , umbral, 255, Imgproc.THRESH_BINARY);

// convert back to bitmap for displaying
Bitmap resultBitmap = Bitmap.createBitmap(mRgba.cols(), mRgba.rows(), Bitmap.Config.ARGB_8888);
blackAndWhiteMat.convertTo(blackAndWhiteMat, CvType.CV_8UC1);
Utils.matToBitmap(blackAndWhiteMat, resultBitmap);
/**************************************************************************/

This may not be the best way but it works.

Now I want to detect black blobs (ink splits). I guess they are detected because the Logcat (log entry of sample app) throws the number of contours detected, but I'm not able to see them because the image is black and white and I want the contour to be red, for example.

Here's an example image:- enter image description here

And here is what I get using RGB (color-blob-detection as is, not black and white image). Notice how small blobs are not detected. (Is it possible to detect them? or are they too small?) enter image description here

Thanks for your help! If you need more info I would gladly update this question

UPDATE: GitHub repo of color-blob-detection sample (second image)

GitHub Repo of openCV sample for Android


Solution

  • The solution is based on a combination of adaptive Image thresholding and use of the connected-component algorithm.

    Assumption - The paper is the most lit area of the image whereas the ink spots on the paper are darkest regions.

    from random import Random
    import numpy as np
    import cv2
    
    def random_color(random):
        """
        Return a random color
        """
        icolor = random.randint(0, 0xFFFFFF)
        return [icolor & 0xff, (icolor >> 8) & 0xff, (icolor >> 16) & 0xff]
    
    #Read as Grayscale
    img = cv2.imread('1-input.jpg', 0)
    cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
    
    # Gaussian to remove noisy region, comment to see its affect.
    img = cv2.medianBlur(img,5)
    
    #Find average intensity to distinguish paper region
    avgPixelIntensity = cv2.mean( img )
    print "Average intensity of image: ", avgPixelIntensity[0]
    
    # Generate mask to distinguish paper region
    #0.8 - used to ignore ill-illuminated region of paper
    mask = cv2.inRange(img, avgPixelIntensity[0]*0.8, 255) 
    mask = 255 - mask
    cv2.imwrite('2-maskedImg.jpg', mask)
    
    #Approach 1
    # You need to choose 4 or 8 for connectivity type(border pixels)
    connectivity = 8
    # Perform the operation
    output = cv2.connectedComponentsWithStats(mask, connectivity, cv2.CV_8U)
    # The first cell is the number of labels
    num_labels = output[0]
    # The second cell is the label matrix
    labels = output[1]
    # The third cell is the stat matrix
    stats = output[2]
    # The fourth cell is the centroid matrix
    centroids = output[3]
    
    cv2.imwrite("3-connectedcomponent.jpg", labels)
    print "Number of labels", num_labels, labels
    
    # create the random number
    random = Random()
    
    for i in range(1, num_labels):
        print stats[i, cv2.CC_STAT_LEFT], stats[i, cv2.CC_STAT_TOP], stats[i, cv2.CC_STAT_WIDTH], stats[i, cv2.CC_STAT_HEIGHT]
        cv2.rectangle(cimg, (stats[i, cv2.CC_STAT_LEFT], stats[i, cv2.CC_STAT_TOP]), 
            (stats[i, cv2.CC_STAT_LEFT] + stats[i, cv2.CC_STAT_WIDTH], stats[i, cv2.CC_STAT_TOP] + stats[i, cv2.CC_STAT_HEIGHT]), random_color(random), 2)
    
    cv2.imwrite("4-OutputImage.jpg", cimg)
    

    The Input Image

    The Input Image

    Masked Image from thresholding and invert operation.

    Masked Image

    Use of connected component.

    CC Image

    Overlaying output of connected component on input image.

    Output Image