سؤال

I wrote a little script to transform pictures of chalkboards into a form that I can print off and mark up.

I take an image like this:

enter image description here

Auto-crop it, and binarize it. Here's the output of the script:

enter image description here

I would like to remove the largest connected black regions from the image. Is there a simple way to do this?

I was thinking of eroding the image to eliminate the text and then subtracting the eroded image from the original binarized image, but I can't help thinking that there's a more appropriate method.

هل كانت مفيدة؟

المحلول

Sure you can just get connected components (of certain size) with findContours or floodFill, and erase them leaving some smear. However, if you like to do it right you would think about why do you have the black area in the first place.

You did not use adaptive thresholding (locally adaptive) and this made your output sensitive to shading. Try not to get the black region in the first place by running something like this:

Mat img = imread("desk.jpg", 0);
Mat img2, dst;
pyrDown(img, img2);
adaptiveThreshold(255-img2, dst, 255,  ADAPTIVE_THRESH_MEAN_C,
        THRESH_BINARY, 9, 10); imwrite("adaptiveT.png", dst);
imshow("dst", dst);
waitKey(-1);

enter image description here

In the future, you may read something about adaptive thresholds and how to sample colors locally. I personally found it useful to sample binary colors orthogonally to the image gradient (that is on the both sides of it). This way the samples of white and black are of equal size which is a big deal since typically there are more background color which biases estimation. Using SWT and MSER may give you even more ideas about text segmentation.

نصائح أخرى

I tried this:

import numpy as np
import cv2

im = cv2.imread('image.png')

gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
grayout = 255*np.ones((im.shape[0],im.shape[1],1), np.uint8)
blur = cv2.GaussianBlur(gray,(5,5),1)
thresh = cv2.adaptiveThreshold(blur,255,1,1,11,2)
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
wcnt = 0
for item in contours:
    area =cv2.contourArea(item) 
    print wcnt,area
    [x,y,w,h] = cv2.boundingRect(item)
    if area>10 and area<200:
        roi = gray[y:y+h,x:x+w]

        cntd = 0
        for i in range(x,x+w):
            for j in range(y,y+h):
                if gray[j,i]==0:
                    cntd = cntd + 1
        density = cntd/(float(h*w))

        if density<0.5:
            for i in range(x,x+w):
                for j in range(y,y+h):
                    grayout[j,i] = gray[j,i];
        wcnt = wcnt + 1

cv2.imwrite('result.png',grayout)

You have to balance two things, removing the black spots but balance that with not losing the contents of what is on the board. The output I got is this:

enter image description here

Here is a Python numpy implementation (using my own mahotas package) of the method for the top answer (almost the same, I think):

import mahotas as mh
import numpy as np

Imported mahotas & numpy with standard abbreviations

im = mh.imread('7Esco.jpg', as_grey=1)

Load the image & convert to gray

im2 = im[::2,::2]
im2 = mh.gaussian_filter(im2, 1.4)

Downsample and blur (for speed and noise removal).

im2 = 255 - im2

Invert the image

mean_filtered = mh.convolve(im2.astype(float), np.ones((9,9))/81.)

Mean filtering is implemented "by hand" with a convolution.

imc = im2 > mean_filtered - 4

You might need to adjust the number 4 here, but it worked well for this image.

mh.imsave('binarized.png', (imc*255).astype(np.uint8))

Convert to 8 bits and save in PNG format.

Result of algorithm

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top