Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filter out small grid lines from an image using python

I am trying to clear out the grids so that I can use the plot alone in a different process. I am trying to solve it using opencv. I have both actual and inverted image. I am not expert in python. Any help could be great.

Thanks in advance

Actual image

enter image description here

inverted image

enter image description here

like image 769
get2anc Avatar asked Sep 20 '25 20:09

get2anc


2 Answers

You can use the opening or closing of your image (depending if you are using the normal or inverted image). Opening will first erode your image and then dilate it. This will remove small/thin objects assuming bright objects over black background.

For example, in the case of your inverted image, use

out = cv2.morphologyEx(src, MORPH_OPEN) 

For more information check out this tutorial

like image 104
gilad Avatar answered Sep 22 '25 09:09

gilad


Here's an approach that uses a combination of filtering techniques and masks.

  • Convert image to grayscale and median blur
  • Adaptive threshold image
  • Perform morphological transformations
  • Find contours and filter using contour area
  • Create a mask to keep the desired ROI sections
  • Bitwise-and to extract plot

Here's the result

enter image description here

import cv2
import numpy as np

image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.medianBlur(gray, 15)

thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,11,3)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
erode = cv2.erode(thresh, kernel, iterations=1)
dilate = cv2.dilate(erode, kernel, iterations=3)

cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

mask = np.zeros(image.shape, dtype=np.uint8)
for c in cnts:
    area = cv2.contourArea(c)
    if area > 850:
        cv2.drawContours(mask, [c], -1, (255,255,255), -1)

mask = cv2.dilate(mask, kernel, iterations=1)
image = 255 - image
result = 255 - cv2.bitwise_and(mask, image)

cv2.imshow('result', result)
cv2.waitKey(0)

2nd Approach

Here's an alternative approach which is the same as the 1st approach but uses specialized horizontal and vertical kernels for filtering instead. This approach is probably more robust. Instead of using guess and check morphological transformations, we have dedicated kernels that filter out the horizontal/vertical grid lines.

vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,5))
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,1))

Here's the mask result after going through each kernel

enter image description here enter image description here

The result is pretty much the same but slightly cleaner :)

enter image description here

import cv2
import numpy as np

image = cv2.imread('1.png',0)
blur = cv2.GaussianBlur(image, (5,5), 0)
thresh = cv2.threshold(blur, 130, 255, cv2.THRESH_BINARY_INV)[1]

vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,5))
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,1))
remove_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel)
remove_vertical = cv2.morphologyEx(remove_horizontal, cv2.MORPH_OPEN, horizontal_kernel)

cnts = cv2.findContours(remove_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

mask = np.ones(image.shape, dtype=np.uint8)
for c in cnts:
    area = cv2.contourArea(c)
    if area > 50:
        cv2.drawContours(mask, [c], -1, (255,255,255), -1)

kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
mask = cv2.dilate(mask, kernel, iterations=1)
image = 255 - image
result = 255 - cv2.bitwise_and(mask, image)

cv2.imshow('result', result)
cv2.imwrite('result.png', result)
cv2.imshow('mask', mask)
cv2.waitKey(0)
like image 42
nathancy Avatar answered Sep 22 '25 11:09

nathancy