How to preprocess an image to remove noise and extract text Python? - python

I have a really noisy image that I have to perform OCR on. The snippet attached is part of a bigger image. How would I go about pre-processing this image in the most optimal way?
I have already tried pre-processing the image using Otsu Binarization, smoothing the image using various filters and Erosion-Dilation. I've also used connectedComponentWithStats to remove the noise in the image. But none of this helps with the processing of the smudged text
Edit - This text needs to be pre-processed in order to perform OCR
img = cv2.imread(file,0)
gaus = cv2.GaussianBlur(img,(5,5),0)
_, blackAndWhite = cv2.threshold(gaus, 127, 255, cv2.THRESH_BINARY_INV)
nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats(blackAndWhite, None, None, None, 8, cv2.CV_32S)
sizes = stats[1:, -1]
img2 = np.zeros((labels.shape), np.uint8)
for i in range(0, nlabels - 1):
if sizes[i] >= 50:
img2[labels == i + 1] = 255
res = cv2.bitwise_not(img2)
(thresh, img_bin) = cv2.threshold(img, 128, 255,cv2.THRESH_BINARY| cv2.THRESH_OTSU)
img_bin = 255-img_bin
kernel_length = np.array(img).shape[1]//80
verticle_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, kernel_length))
hori_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_length, 1))
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
img_temp1 = cv2.erode(img_bin, verticle_kernel, iterations=3)
verticle_lines_img = cv2.dilate(img_temp1, verticle_kernel, iterations=3)
img_temp2 = cv2.erode(img_bin, hori_kernel, iterations=3)
horizontal_lines_img = cv2.dilate(img_temp2, hori_kernel, iterations=3)
alpha = 0.5
beta = 1.0 - alpha
img_final_bin = cv2.addWeighted(verticle_lines_img, alpha, horizontal_lines_img, beta, 0.0)
img_final_bin = cv2.erode(~img_final_bin, kernel, iterations=2)
(thresh, img_final_bin) = cv2.threshold(img_final_bin, 128,255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

Here's an approach to remove the noise
Convert image to grayscale and Otsu's threshold
Perform morphological transformations to smooth image
Find contours and filter using contour area
Invert image
After converting to grayscale, we Otsu's threshold to obtain a binary image
From here we create a kernel and perform morphological opening to smooth the image. You could try using different kernels sizes here to remove more noise but increasing the kernel size will also remove text detail
Next we find contours and filter using contour area with a maximum threshold area to remove the small particles. We fill in the contour to effectively remove the noise
Finally we invert the image to get our result
import cv2
import numpy as np
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=1)
cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
area = cv2.contourArea(c)
if area < 150:
cv2.drawContours(opening, [c], -1, (0,0,0), -1)
result = 255 - opening
cv2.imshow('thresh', thresh)
cv2.imshow('opening', opening)
cv2.imshow('result', result)
cv2.waitKey()

Related

How to detect checkboxes by removing noise using Python OpenCV?

I am trying to identify the checkboxes in the image
The top 4 are identified but the bottom 2 are not. At the same time I would like to be able to get rid of the peppering to avoid false positives as there are other docs that have checkmarks that are much smaller. I've tried various dilation and kernel sizes but I haven't been able to successful get the box.
I've tried to dilate it and then erode it
kernel = np.ones((2, 2), np.uint8)
image_dilat = cv2.dilate(image, kernel, iterations=1)
kernel = np.ones((4, 4), np.uint8)
image_erosion = cv2.erode(image_dilat2, kernel, iterations=1)
I've tried morphing it as well
kernel = np.ones((3, 3), np.uint8)
image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel, iterations=1)
kernel = np.ones((3, 3), np.uint8)
image = cv2.morphologyEx(image, cv2.cv2.MORPH_CLOSE, , kernel, iterations=1)
Any suggestion will be appreciated.
Here's a potential approach using simple image processing:
Obtain binary image. Load the image, convert to grayscale, and Otsu's threshold.
Remove small pixels of noise. Find contours and filter out noise using contour area filtering. We effectively remove the noise by "drawing in" the contours with black.
Repair checkbox walls. From here we create a horizontal and vertical repair kernel then perform morphological close to fix any holes in the checkbox walls.
Detect checkboxes. Next find contours on the repaired image then filter for checkbox contours using shape approximation and aspect ratio filtering. The idea is that a checkbox is a square and should have roughly the same width and height.
Binary image with noise -> Removed tiny noise
Repaired checkbox walls -> Detected checkboxes
Code
import cv2
# Load image, convert to grayscale, Otsu's threshold
image = cv2.imread('1.png')
original = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
cv2.imshow('thresh before', thresh)
# Find contours and filter using contour area filtering to remove noise
cnts, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[-2:]
AREA_THRESHOLD = 10
for c in cnts:
area = cv2.contourArea(c)
if area < AREA_THRESHOLD:
cv2.drawContours(thresh, [c], -1, 0, -1)
# Repair checkbox horizontal and vertical walls
repair_kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (5,1))
repair = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, repair_kernel1, iterations=1)
repair_kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (1,5))
repair = cv2.morphologyEx(repair, cv2.MORPH_CLOSE, repair_kernel2, iterations=1)
# Detect checkboxes using shape approximation and aspect ratio filtering
cnts, _ = cv2.findContours(repair, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2:]
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.05 * peri, True)
x,y,w,h = cv2.boundingRect(approx)
aspect_ratio = w / float(h)
if aspect_ratio > 0.9 and aspect_ratio < 1.1:
cv2.rectangle(original, (x, y), (x + w, y + h), (36,255,12), 3)
cv2.imshow('thresh', thresh)
cv2.imshow('repair', repair)
cv2.imshow('original', original)
cv2.waitKey()
Note: The assumption is that the checkboxes are square shaped and that there are no noise overlapping the checkboxes. Depending on the image, you may want to add another layer of contour area filtering to ensure that you don't get false positives.

How to extract only brain part in center in MRI image? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I have a MRI image of brain. I need to remove cranium (skull) from MRI and then crop background region which is around brain. How could I do that in python with image processing ?. I have tried using openCV
This is the code which I tried:
def crop_brain_contour(image, plot=False):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_OTSU)
ret, markers = cv2.connectedComponents(thresh)
marker_area = [np.sum(markers==m) for m in range(np.max(markers)) if m!=0]
largest_component = np.argmax(marker_area)+1
brain_mask = markers==largest_component
brain_out = image.copy()
brain_out[brain_mask==False] = (0,0,0)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.threshold(gray, 45, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.erode(thresh, None, iterations=2)
thresh = cv2.dilate(thresh, None, iterations=2)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
c = max(cnts, key=cv2.contourArea)
# extreme points
extLeft = tuple(c[c[:, :, 0].argmin()][0])
extRight = tuple(c[c[:, :, 0].argmax()][0])
extTop = tuple(c[c[:, :, 1].argmin()][0])
extBot = tuple(c[c[:, :, 1].argmax()][0])
new_image = image[extTop[1]:extBot[1], extLeft[0]:extRight[0]]
return new_image
These images are similar that i required:
When i run this code i get this image
Thank you for the help!!
Here is one approach in Python/OpenCV.
- Read the input
- Convert to grayscale
- Threshold
- Apply morphology close
- Get the largest contour
- Draw the largest contour as white filled on a black background as a mask
- OPTIONALLY: erode the mask
- Get the dimensions of the contour (after optional eroding)
- Crop the input image and mask to those dimensions
- Put the mask into the alpha channel of the image to make the outside transparent
- Save the results
import cv2
import numpy as np
# load image
img = cv2.imread('mri.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# apply morphology
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# get external contour
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
# draw white filled contour on black background as mask
mask = np.zeros_like(thresh, dtype=np.uint8)
cv2.drawContours(mask, [big_contour], 0, 255, -1)
# get bounds of contour
x,y,w,h = cv2.boundingRect(big_contour)
# crop image and mask
img_crop = img[y:y+h, x:x+w]
mask_crop = mask[y:y+h, x:x+w]
# put mask in alpha channel of image
result = cv2.cvtColor(img_crop, cv2.COLOR_BGR2BGRA)
result[:,:,3] = mask_crop
# save resulting masked image
cv2.imwrite('mri_thresh.png', thresh)
cv2.imwrite('mri_cropped.png', img_crop)
cv2.imwrite('mri_cropped_alpha.png', result)
# ALTERNATE ERODE mask
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
thresh2 = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
# get external contour
contours2 = cv2.findContours(thresh2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours2 = contours2[0] if len(contours2) == 2 else contours2[1]
big_contour2 = max(contours2, key=cv2.contourArea)
# draw white filled contour on black background as mask
mask2 = np.zeros_like(thresh2, dtype=np.uint8)
cv2.drawContours(mask2, [big_contour2], 0, 255, -1)
# get bounds of contour
x,y,w,h = cv2.boundingRect(big_contour2)
# crop image and mask
img_crop2 = img[y:y+h, x:x+w]
mask_crop2 = mask2[y:y+h, x:x+w]
# put mask in alpha channel of image
result2 = cv2.cvtColor(img_crop2, cv2.COLOR_BGR2BGRA)
result2[:,:,3] = mask_crop2
# save results
cv2.imwrite("mri_thresh.png", thresh)
cv2.imwrite("mri_cropped.png", img_crop)
cv2.imwrite("mri_cropped_alpha.png", result)
cv2.imwrite("mri_thresh2.png", thresh2)
cv2.imwrite("mri_cropped2.png", img_crop2)
cv2.imwrite("mri_cropped_alpha2.png", result2)
# display result
cv2.imshow("thresh", thresh)
cv2.imshow("mask", mask)
cv2.imshow("result", result)
cv2.imshow("thresh2", thresh2)
cv2.imshow("mask2", mask2)
cv2.imshow("result2", result2)
cv2.waitKey(0)
cv2.destroyAllWindows()
Input:
Threshold image:
Mask image:
Simple crop of image:
Cropped image with alpha channel:
Cropped image with alpha channel with optional erode:

How to calculate similarity between two signature images

I have signature images like this one:
Both of these signatures are similar and I need to print if they are similar or not.
I have tried SSIM, MSE, SIFT and Cosine similarity.
SSIM gives almost more than 0.7 index for both similar and dissimilar images.
MSE gives a very large distance once these are cleaned and compared
SIFT gave 66 features matches
And cosine similarity gave 0.08
Here is the code that helps clean the images
image = cv2.imread('test2.png')
image = cv2.resize(image, (680, 460))
image = cv2.detailEnhance(image)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (1,1), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Filter using contour area and remove small noise
cnts = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
area = cv2.contourArea(c)
if area < 5500:
cv2.drawContours(thresh, [c], -1, (0,0,0), -1)
# Morph close and invert image
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,1))
close = 255 - cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=1)
cv2.imshow('thresh', close) ###for showing
cv2.waitKey()
kernel = np.ones((1,1),np.uint8)
# sign2 = cv2.dilate(close,kernel,iterations = 1)
sign2= close
# cv2.imshow('thresh', sign2) ###for showing
# cv2.waitKey()
I clean both images and then compare.
Is there any other way to compare them. All I want is if they are similar then just say yes :p

How to detect paragraphs in a text document image for a non-consistent text structure in Python OpenCV

I am trying to identify paragraphs of text in a .pdf document by first converting it into an image then using OpenCV. But I am getting bounding boxes on lines of text instead of paragraphs. How can I set some threshold or some other limit to get paragraphs instead of lines?
Here is the sample input image:
Here is the output I am getting for the above sample:
I am trying to get a single bounding box on the paragraph in the middle. I am using this code.
import cv2
import numpy as np
large = cv2.imread('sample image.png')
rgb = cv2.pyrDown(large)
small = cv2.cvtColor(rgb, cv2.COLOR_BGR2GRAY)
# kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
kernel = np.ones((5, 5), np.uint8)
grad = cv2.morphologyEx(small, cv2.MORPH_GRADIENT, kernel)
_, bw = cv2.threshold(grad, 0.0, 255.0, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 1))
connected = cv2.morphologyEx(bw, cv2.MORPH_CLOSE, kernel)
# using RETR_EXTERNAL instead of RETR_CCOMP
contours, hierarchy = cv2.findContours(connected.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#For opencv 3+ comment the previous line and uncomment the following line
#_, contours, hierarchy = cv2.findContours(connected.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
mask = np.zeros(bw.shape, dtype=np.uint8)
for idx in range(len(contours)):
x, y, w, h = cv2.boundingRect(contours[idx])
mask[y:y+h, x:x+w] = 0
cv2.drawContours(mask, contours, idx, (255, 255, 255), -1)
r = float(cv2.countNonZero(mask[y:y+h, x:x+w])) / (w * h)
if r > 0.45 and w > 8 and h > 8:
cv2.rectangle(rgb, (x, y), (x+w-1, y+h-1), (0, 255, 0), 2)
cv2.imshow('rects', rgb)
cv2.waitKey(0)
This is a classic situation for dilate. Whenever you want to connect multiple items together, you can dilate them to join adjacent contours into a single contour. Here's a simple approach:
Obtain binary image. Load the image, convert to grayscale, Gaussian blur, then Otsu's threshold to obtain a binary image.
Connect adjacent words together. We create a rectangular kernel and dilate to merge individual contours together.
Detect paragraphs. From here we find contours, obtain the rectangular bounding rectangle coordinates and highlight the rectangular contours.
Otsu's threshold to obtain a binary image
Here's where the magic happens. We can assume that a paragraph is a section of words that are close together, to achieve this we dilate to connect adjacent words
Result
import cv2
import numpy as np
# Load image, grayscale, Gaussian blur, Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (7,7), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Create rectangular structuring element and dilate
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
dilate = cv2.dilate(thresh, kernel, iterations=4)
# Find contours and draw rectangle
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
x,y,w,h = cv2.boundingRect(c)
cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 2)
cv2.imshow('thresh', thresh)
cv2.imshow('dilate', dilate)
cv2.imshow('image', image)
cv2.waitKey()

How to extract multiple objects from an image using Python OpenCV?

I am trying to extract object from an image using the color using OpenCV, I have tried by inverse thresholding and grayscale combined with cv2.findContours() but I am unable to use it recursively. Furthermore I can't figure out how to "cut out" the match from the original image and save it to a single file.
EDIT
~
import cv2
import numpy as np
# load the images
empty = cv2.imread("empty.jpg")
full = cv2.imread("test.jpg")
# save color copy for visualization
full_c = full.copy()
# convert to grayscale
empty_g = cv2.cvtColor(empty, cv2.COLOR_BGR2GRAY)
full_g = cv2.cvtColor(full, cv2.COLOR_BGR2GRAY)
empty_g = cv2.GaussianBlur(empty_g, (51, 51), 0)
full_g = cv2.GaussianBlur(full_g, (51, 51), 0)
diff = full_g - empty_g
# thresholding
diff_th =
cv2.adaptiveThreshold(full_g,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY,11,2)
# combine the difference image and the inverse threshold
zone = cv2.bitwise_and(diff, diff_th, None)
# threshold to get the mask instead of gray pixels
_, zone = cv2.threshold(bag, 100, 255, 0)
# dilate to account for the blurring in the beginning
kernel = np.ones((15, 15), np.uint8)
bag = cv2.dilate(bag, kernel, iterations=1)
# find contours, sort and draw the biggest one
contours, _ = cv2.findContours(bag, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
contours = sorted(contours, key=cv2.contourArea, reverse=True)[:3]
i = 0
while i < len(contours):
x, y, width, height = cv2.boundingRect(contours[i])
roi = full_c[y:y+height, x:x+width]
cv2.imwrite("piece"+str(i)+".png", roi)
i += 1
Where empty is just a white image size 1500 * 1000 as the one above and test is the one above.
This is what I came up with, only downside, I have a third image instead of only the 2 expected showing a shadow zone now...
Here's a simple approach:
Obtain binary image. Load the image, grayscale, Gaussian blur, Otsu's threshold, then dilate to obtain a binary black/white image.
Extract ROI. Find contours, obtain bounding boxes, extract ROI using Numpy slicing, and save each ROI
Binary image (Otsu's thresholding + dilation)
Detected ROIs highlighted in green
To extract each ROI, you can find the bounding box coordinates using cv2.boundingRect(), crop the desired region, then save the image
x,y,w,h = cv2.boundingRect(c)
ROI = original[y:y+h, x:x+w]
First object
Second object
import cv2
# Load image, grayscale, Gaussian blur, Otsu's threshold, dilate
image = cv2.imread('1.jpg')
original = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
dilate = cv2.dilate(thresh, kernel, iterations=1)
# Find contours, obtain bounding box coordinates, and extract ROI
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
image_number = 0
for c in cnts:
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('image', image)
cv2.imshow('thresh', thresh)
cv2.imshow('dilate', dilate)
cv2.waitKey()

Categories

Resources