1

I have a collection of images as below -

Example 1

Example 2

Example 3

These represent dates in DDMMYYYY format. For each of these images, I want to save each digit as a separate image.For example 1, I wish to save 7,9,0,8,5,8,7,1 as separate images sliced from the original image. So far, I have tried various methods described on different stackoverflow & blogposts but none of them seems to work.

Code to extract boxes surrounding dates -

from glob import glob

import cv2 as cv
import numpy as np
from tqdm import tqdm


class ExtractRectangle:
    def __init__(self):
        super().__init__()

        self.minLinLength_h = 70
        self.minLinLength_v = 5
        self.maxLineGap = 20

    def is_horizontal(self, line, thresh=5):
        return abs(line[1] - line[3]) <= thresh

    def is_vertical(self, line, thresh=5):
        return abs(line[0] - line[2]) <= thresh

    def get_lines(self, canny, horizontal=True):
        lines = []
        if horizontal:
            linesP = cv.HoughLinesP(
                canny,
                rho=1,
                theta=np.pi / 180,
                threshold=10,
                lines=None,
                minLineLength=self.minLinLength_h,
                maxLineGap=20,
            )
        else:
            linesP = cv.HoughLinesP(
                canny,
                rho=1,
                theta=np.pi / 180,
                threshold=10,
                lines=None,
                minLineLength=self.minLinLength_v,
                maxLineGap=20,
            )
        if linesP is not None:
            for i in range(0, len(linesP)):
                l = linesP[i][0]
                if self.is_horizontal(l, 3) and horizontal:
                    lines.append(l)
                elif self.is_vertical(l, 3):
                    lines.append(l)
        return lines

    def remove_whitespace(self, img):
        # https://stackoverflow.com/questions/48395434/how-to-crop-or-remove-white-background-from-an-image
        gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
        th, threshed = cv.threshold(gray, 127, 255, cv.THRESH_BINARY_INV)

        kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (11, 11))
        morphed = cv.morphologyEx(threshed, cv.MORPH_CLOSE, kernel)

        cnts = cv.findContours(morphed, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[-2]
        cnt = sorted(cnts, key=cv.contourArea)[-1]

        x, y, w, h = cv.boundingRect(cnt)
        dst = img[y : y + h, x : x + w]
        return dst

    def process_image(self, filename, path):
        errenous = False
        img = cv.imread(cv.samples.findFile(filename))
        img = self.remove_whitespace(img)
        cImage = np.copy(img)

        gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
        canny = cv.Canny(gray, 100, 200)

        horizontal_lines = self.get_lines(canny)
        horizontal_lines = sorted(horizontal_lines, key=lambda a_entry: a_entry[..., 1])

        vertical_lines = self.get_lines(canny, horizontal=False)
        vertical_lines = sorted(vertical_lines, key=lambda a_entry: a_entry[..., 0])

        if len(horizontal_lines) > 0:
            initial_line = horizontal_lines[0]
            final_line = horizontal_lines[-1]

            # LeftTop(x1, y1) -> RightTop(x2, y1) -> RightBottom(x2, y2) -> LeftBottom(x1, y2)
            y1 = initial_line[1]
            y2 = final_line[1]
            bottom = min(y1, y2)
            top = max(y1, y2)

            # post whitespace removal, dates should only be the major component
            if (top-bottom) / img.shape[0] < 0.6:
                errenous = True
        else:
            errenous = True

        if len(vertical_lines) > 0:
            initial_line = vertical_lines[0]
            final_line = vertical_lines[-1]

            x1 = initial_line[0]
            x2 = final_line[0]
            left = min(x1, x2)
            right = max(x1, x2)

            # as dates occupy majority of the horizontal space
            if (right-left) / img.shape[1] < 0.95:
                errenous = True
        else:
            errenous = True

        if not errenous:
            # cImage = cv.rectangle(cImage, (left, bottom), (right, top), (255, 0, 0), 2)
            cImage = cImage[
                bottom : bottom + (top - bottom), left : left + (right - left)
            ]
        cv.imwrite(f"{path}/{filename.split('/')[-1]}", cImage)


if __name__ == "__main__":
    extract = ExtractRectangle()
    test_files = glob("data/raw/test/*.png")
    test_path = "data/processed/test/"
    for path in tqdm(test_files):
        extract.process_image(path, test_path)

    train_files = glob("data/raw/train/*.png")
    train_path = "data/processed/train/"
    for path in tqdm(train_files):
        extract.process_image(path, train_path)

Resultant detection for above images -

Example 1

Example 2

Example 3

Some other samples

Aditya Mishra
  • 1,687
  • 2
  • 15
  • 24
  • 1
    first find the 2 horizontal lines based on there location find the vertical limiting boxes and segment that into 8 separate images. If your scans are really that poor quality (variation) you will have a tough time solving this – rioV8 Oct 26 '20 at 11:46
  • Ok! Will try that! Is it possible to eliminate the text surrounding the actual digits? – Aditya Mishra Oct 26 '20 at 11:56
  • 1
    @AdityaMishra Detect the **longest lines** using **Hough Transform**, then from the coorrdinates of the **2 ends** of the **2 longest lines** you have 4 points to describe your boundary box as **ROI**. – Bilal Oct 27 '20 at 08:09
  • Thanks @Bilal that's what I tried. However, I am facing issues as Hough Transform is failing to detect the vertical lines properly – Aditya Mishra Oct 27 '20 at 15:59
  • @AdityaMishra in order to help solve your issues, you have to provide the [Minimal Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) – Bilal Oct 27 '20 at 19:53
  • @Bilal I have added code for edge detections & some example images – Aditya Mishra Oct 28 '20 at 09:17

0 Answers0