How to segment characters and words from images into contours

I have a few contour images on which I want to do segmentation which basically means I want to save all characters in a contour image into individual images. But I am getting several noise images along with the required output. I want to know how to remove all the noise images without affecting the required output.

I was trying to change the values of w and h, so that I can minimize the noise and get only characters as segmented images.

def imageSegmentation(fldr):
        for file in fldr:
            for f in os.listdir(file):
                im = cv2.imread(file+f)
                #print(f)
                imgray=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
                ret, thresh = cv2.threshold(imgray, 127, 255, 0)
                contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
                con_img=cv2.drawContours(im, contours, -1, (0,0,0), 1)
                #cv2.imshow("Contour_Image",con_img)
                #cv2.waitKey(0)
                #cv2.destroyAllWindows()

                newfolder=file+"\\contour\\"+f+"\\"
                os.makedirs(newfolder, exist_ok=True)
                fname=os.path.splitext(f)[0]
                cv2.imwrite((newfolder+fname+".png"),con_img)
                #cv2.imshow("con_img",con_img)
                #cv2.waitKey()
                #cv2.destroyAllWindows()

                newfolder2=file+"\\seg\\"+fname+"\\"
                os.makedirs(newfolder2,exist_ok=True)
                sorted_ctrs = sorted(contours, key=lambda cntr: cv2.boundingRect(cntr)[0])

                for i, cntr in enumerate(sorted_ctrs):
                    # Get bounding box
                    x, y, w, h = cv2.boundingRect(cntr)

                    # Getting ROI
                    roi = im[y:y + h, x:x + w]
                    #roi=~roi

                    if w > 9 and h > 27:
                        cv2.imwrite(newfolder2+"{}.png".format(i), roi)

I want to know how to only get correct characters images excluding the noise images in output folder. I have added a few of my input contour images which I need to segment into individual characters.

1

2

3

4

5

6

1 answer

  • answered 2019-06-21 21:08 nathancy

    Since your question is not completely clear if you wanted to extract individual characters or whole words, here is the approach to do both.

    Individual characters

    The main idea here is

    • Convert image to grayscale and gaussian blur
    • Perform canny edge detection
    • Find contours
    • Iterate through contours and filter using a minimum area
    • Obtain bounding boxes and extract ROI

    Canny edge detection using cv2.Canny()

    enter image description here

    Now we iterate through contours using cv2.findContours() and filter using cv2.contourArea() then draw bounding boxes enter image description here

    Here's the results for some of your other input images

    enter image description here enter image description here

    enter image description here enter image description here

    import cv2
    
    image = cv2.imread('1.png')
    original = image.copy()
    
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (3,3), 0)
    canny = cv2.Canny(blur, 120, 255, 1)
    
    cnts = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    
    min_area = 100
    image_number = 0
    for c in cnts:
        area = cv2.contourArea(c)
        if area > min_area:
            x,y,w,h = cv2.boundingRect(c)
            cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 2)
            ROI = original[y:y+h, x:x+w]
            cv2.imwrite("ROI_{}.png".format(image_number), ROI)
            image_number += 1
    
    cv2.imshow('blur', blur)
    cv2.imshow('canny', canny)
    cv2.imshow('image', image)
    cv2.waitKey(0)
    

    Whole words

    Now if you want to extract whole words, you have to modify the strategy a bit

    • Convert image to grayscale and gaussian blur
    • Perform canny edge detection
    • Dilate to obtain a single contour
    • Find contours
    • Iterate through contours and filter using a minimum area
    • Obtain bounding boxes and extract ROI

    Canny edge detection

    enter image description here

    Dilate using cv2.dilate() to connect contours

    enter image description here

    Find bounding boxes and filter using contour area

    enter image description here

    Extracted ROI

    enter image description here

    Note: If you're trying to find whole words, you may have to change the minimum area value since it's dependent on the image you're analyzing.

    import cv2
    
    image = cv2.imread('1.png')
    original = image.copy()
    
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (3,3), 0)
    canny = cv2.Canny(blur, 120, 255, 1)
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
    dilate = cv2.dilate(canny, kernel, iterations=5)
    cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    
    min_area = 5000
    image_number = 0
    for c in cnts:
        area = cv2.contourArea(c)
        if area > min_area:
            x,y,w,h = cv2.boundingRect(c)
            cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 2)
            ROI = original[y:y+h, x:x+w]
            cv2.imwrite("ROI_{}.png".format(image_number), ROI)
            image_number += 1
    
    cv2.imshow('blur', blur)
    cv2.imshow('dilate', dilate)
    cv2.imshow('canny', canny)
    cv2.imshow('image', image)
    cv2.waitKey(0)