get2anc
get2anc

Reputation: 41

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

Upvotes: 4

Views: 2453

Answers (3)

fmw42
fmw42

Reputation: 53081

Here is a pretty simple solution using Imagemagick. But the same concepts can be use from OpenCV. Sorry, I am not fluent with OpenCV.

Threshold the image

Perform morphologic close

Use connected components processing to remove extraneous features 


Input:

enter image description here

kernel="5x5: 0,0,0,0,0 1,1,1,1,1 1,1,1,1,1 1,1,1,1,1 0,0,0,0,0"
convert img.png -threshold 75% \
-morphology close "$kernel" \
-define connected-components:area-threshold=100 \
-define connected-components:mean-color=true \
-connected-components 4 result.png


enter image description here

ADDITION:

Here is Python Wand code to do the same thing. Python Wand is based upon Imagemagick. It will require Wand 0.5.6 (when available) and Imagemagick 7.

#!/bin/python3.7

from wand.image import Image

with Image(filename='curve.png') as img:
    krnl="5x5: 0,0,0,0,0 1,1,1,1,1 1,1,1,1,1 1,1,1,1,1 0,0,0,0,0"
    img.threshold(threshold=0.75)
    img.morphology(method='close',kernel=krnl)
    img.connected_components(connectivity=4, area_threshold=100, mean_color=True)
    img.save(filename='curve_proc.png')

Upvotes: 2

nathancy
nathancy

Reputation: 46600

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


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)

Upvotes: 2

gilad
gilad

Reputation: 436

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

Upvotes: 2

Related Questions