How can i remove background noise from a handwritten text image? - opencv

I tried these approaches but didn't get any real changes. actually, I am trying to build a handwritten OCR using Google cloud vision API. please suggest to me what can I do for preprocessing steps.
1.
image = cv2.fastNlMeansDenoisingColored(image, None, 10, 10, 7, 15)
kernel = np.ones((5, 5), np.uint8)
image = cv2.dilate(image, kernel, iterations = 1)
kernel = np.ones((5, 5), np.uint8)
image = cv2.erode(image, kernel, iterations = 1)

Another way is HSV color filter. Because you are using blue pen, so we can choice the color that we want. Sample code:
import cv2
import numpy as np
image = cv2.imread('9rS31.jpg')
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower_green = np.array([100, 43, 20])
upper_green = np.array([130, 255, 255])
mask = cv2.inRange(hsv, lower_green, upper_green)
res = cv2.bitwise_and(image, image, mask=mask)
gray = cv2.cvtColor(res,cv2.COLOR_BGR2GRAY)
ret, generator = cv2.threshold(gray, 1,255,cv2.THRESH_BINARY)
cv2.imwrite("img.jpg",generator)
Generated image:

The noise is including horizontal line in your text book. So one method is using
cv2.getStructuringElement
You can find more information on the internet. Sample code:
import cv2
# Load image
image = cv2.imread('9rS31.jpg')
img=image.copy()
# Remove border
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (50,1))
temp2 = 255 - cv2.morphologyEx(image, cv2.MORPH_CLOSE, horizontal_kernel)
result = cv2.add(temp2, image)
# Convert to grayscale and Otsu's threshold
gray = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray,(5,5),0)
_,thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY_INV)
cv2.imwrite('img.jpg',thresh)
cv2.imshow('img', thresh)
cv2.waitKey()
Generated image:

Related

How to improve performance of cell segmentation using watershed algorithm with opencv-python

I have tried to segment cells in H&E-stained histopathological images using Watershed algorithm of opencv-python.
The code I used is totally same as Docs opencv code in link below.
Watershed Code Source
But as you see the result, the performance of segmentation is not that much good.
Some cells could not be detected.
I want to detect all of cells at a time in that image.
In the case of cell in biomedical, I think this is more sensitive than normal object segmentation.
In original code, I added and applied two Blur algorithms before using cv2.morphologyEx().
img = cv2.imread("Path_of_Image")
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# Convert to Binary Image
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
kernel = np.ones((3, 3), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)
sure_bg = cv2.dilate(opening, kernel, iterations=3)
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
ret, sure_fg = cv2.threshold(dist_transform, 0.5*dist_transform.max(), 255, 0)
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg, sure_fg)
ret, markers = cv2.connectedComponents(sure_fg)
markers = markers + 1
markers[unknown == 255] = 0
markers = cv2.watershed(img_rgb, markers)
img_rgb[markers == -1] = [255, 0, 0]
images = [gray, thresh, sure_bg, dist_transform, sure_fg, unknown, markers, img_rgb]
titles = ['Gray','Binary','Sure BG','Distance','Sure FG','Unknow','Markers','Result']
plt.figure(figsize=(12, 12))
for i in range(len(images)):
plt.subplot(2, 4, i + 1),
plt.imshow(images[i], cmap='gray'),
plt.title(titles[i]),
plt.xticks([]),plt.yticks([])
# plt.figure(figsize= (5, 5))
# plt.tight_layout()
plt.show()
There was a litte bit improvement, but still need changes.
How can I deal with this problem? Do I have to more examine Marker value or something?
I wonder your thinking.
Thank you in advance.
[Add]
This is Original Image.
Original Image

Not getting results in OCR - Pytesseract

Just used the following code a OCR application. The OCR to be read is on a metal milled surface with a unique font. The below code work well for embossed surfaces but not engraved surfaces. I have tried tweaking the blur and dilate iterations, still no results.
How i can add new fonts into tesseract , if the unique font is an issue ?
Any tips on how i can get better results ?
import cv2
import numpy as np
import imutils
import pytesseract
# read image from disk
image = cv2.imread('test.jpg')
# make it gray
img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# blur it to remove noise
img = cv2.GaussianBlur(img, (7,7), 0)
# perform edge detection, then perform a dilation + erosion to
# close gaps in between object edges
edged = cv2.Canny(img, 40, 90)
dilate = cv2.dilate(edged, None, iterations=1)
# perform erosion if necessay, it completely depends on the image
#erode = cv2.erode(dilate, None, iterations=1)
# create an empty masks
mask = np.ones(img.shape[:2], dtype="uint8") * 255
# find contours
cnts = cv2.findContours(dilate.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[1] if imutils.is_cv2() else cnts[0]
orig = img.copy()
for c in cnts:
# if the contour is not sufficiently large, ignore it
if cv2.contourArea(c) < 300:
cv2.drawContours(mask, [c], -1, 0, -1)
x,y,w,h = cv2.boundingRect(c)
# filter more contours if nessesary
if(w>h):
cv2.drawContours(mask, [c], -1, 0, -1)
newimage = cv2.bitwise_and(dilate.copy(), dilate.copy(), mask=mask)
img2 = cv2.dilate(newimage, None, iterations=5)
ret2,th1 = cv2.threshold(img2 ,0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
pytesseract.pytesseract.tesseract_cmd = r'root\folder'
# Tesseract OCR on the image
temp = pytesseract.image_to_string(th1)
# Write results on the image
cv2.putText(image, temp, (100,100), cv2.FONT_HERSHEY_SIMPLEX, 1.8, (0,255,255), 3)
# show the outputs
cv2.imshow('Original image', cv2.resize(image,(640,480)))
cv2.imshow('Dilated', cv2.resize(dilate,(640,480)))
cv2.imshow('New Image', cv2.resize(newimage,(640,480)))
cv2.imshow('Inverted Threshold', cv2.resize(th1,(640,480)))
cv2.waitKey(0)
cv2.destroyAllWindows()

Determine flow meter reading from image

currently I'm working on a project to identify the rotameter's reading. My main task is to located the rotameter from the input image, then identify the line mark from the rotameter as well as the floater. Then, I need to find out the reading of the meter based on the position of the floater. There are a few challenges faced, the glare on the meter due to illumination and the angle of the image taken. Currently, I tried with CLAHE followed by Canny edge detection. However, I was only able to get the line mark at the area without light reflection. I had also tried Inpaint + CLAHE to remove the glare part and Gaussian blurring to reduce the image noises. But most on the line marks are missing. I'm still new in computer vision and currently using Python + OpenCV. Any good suggestions?
Source image
Here's the code I still trying on for Inpaint + CLAHE, and the outcome is attached.
Outcome: Inpaint + Clahe
img = cv2.imread('IMG-20210630-WA0016.jpeg')
resized = cv2.resize(img, (540, 960))
crp = resized[0:960, 150:360]
# convert image from RGB to HSV
img_hsv = cv2.cvtColor(crp, cv2.COLOR_RGB2HSV)
# Histogram equalisation on the V-channel
img_hsv[:, :, 2] = cv2.equalizeHist(img_hsv[:, :, 2])
# convert image back from HSV to RGB
image = cv2.cvtColor(img_hsv, cv2.COLOR_HSV2RGB)
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
# Create a CLAHE object (Arguments are optional).
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(4,4))
cl1 = clahe.apply(gray)
cv2.imshow('Clahe', cl1)
# mask = cv2.threshold(cl1, 180, 185, cv2.THRESH_BINARY)[1]
mask = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
# use mask with input to do inpainting
inpaint = cv2.inpaint(cl1, mask, 21, cv2.INPAINT_NS)
# display it
cv2.imshow("IMAGE", crp)
cv2.imshow("GRAY", gray)
result1 = cv2.GaussianBlur(inpaint, (1, 1), 1, cv2.BORDER_DEFAULT)
canny = cv2.Canny(result1, 100, 255)
cv2.imshow("Canny", canny)
key = cv2.waitKey(0)
if key == 27:
cv2.destroyAllWindows()
For this, I just tried with CLAHE, the line marks look clearer but the position of the floater are missing.
Outcome 2: Clahe
img = cv2.imread('IMG-20210630-WA0016.jpeg')
resized = cv2.resize(img, (540, 960))
crp = resized[0:960, 150:360]
gray = cv2.cvtColor(crp,cv2.COLOR_BGR2GRAY)
# Create a CLAHE object (Arguments are optional).
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(4,4))
cl1 = clahe.apply(gray)
result1 = cv2.GaussianBlur(cl1, (3, 3), 1, cv2.BORDER_DEFAULT)
canny = cv2.Canny(result1, 150, 255)
cv2.imshow("Canny", canny)
cv2.imshow("Source", crp)
cv2.imshow('Clahe', cl1)
key = cv2.waitKey(0)
if key == 27:
cv2.destroyAllWindows()

How to whiten background and blaken grid in a same image

I have an image like this. I wan to use HoughLine detection but the image is too dark to recognize the line. Is there a way that can whiten the background and blacken the grid? Is there any algorithms in openCV or python that I can apply? Thank you
I try to dilate the image first, then medianBlur it, so I get the background. Use the original gray image to sub the background, I get the frontground ( that is the grids). Then do some other steps, I get the result like this.
The code is as follow:
#!/usr/bin/python3
# 2017.10.04 19:37:43 CST
filename = "data/paper.png"
img = cv2.imread(filename)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
## do morph-dilate-op
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
dilated = cv2.morphologyEx(gray, cv2.MORPH_DILATE, kernel)
diff1 = 255 - cv2.subtract(dilated, gray)
## do medianBlur
median = cv2.medianBlur(dilated, 15)
diff2 = 255 - cv2.subtract(median, gray)
## do normalize
normed = cv2.normalize(diff2,None, 0, 255, cv2.NORM_MINMAX )
## save the result
dst = np.hstack((gray, normed))
cv2.imwrite("result_paper1.png", dst)
res = np.hstack((gray,dilated, diff1, median, diff2, normed))
cv2.imwrite("result_paper2.png", res)
You should try a form of localized adaptive thresholding.
In OpenCV this is called cv2.adaptiveThreshold.
See here: http://docs.opencv.org/master/d7/d4d/tutorial_py_thresholding.html for a python example.
The code (from the source above):
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('sudoku.png',0)
img = cv2.medianBlur(img,5)
ret,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
th2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,\
cv2.THRESH_BINARY,11,2)
th3 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv2.THRESH_BINARY,11,2)
titles = ['Original Image', 'Global Thresholding (v = 127)',
'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
for i in range(4):
plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
Your image has poor contrast and inconsistent light. You have to make some preprocessing (c++ code here):
cv::Mat img = cv::imread("E:\\Workspace\\KS\\excercise\\oBwBH.jpg", 0);
cv::Mat workingMat;
cv::GaussianBlur(img, workingMat, cv::Size(101, 101), 31, 31); //high blur to extract background light
img = img - 0.7*work; //adjust light level
cv::normalize(img, img, 0, 255, cv::NORM_MINMAX); \\use whole range
cv::medianBlur(img, img, 5); \\remove noise
cv::Canny(img, work, 100, 200); \\extract lines; you could do hough lines instead since it has canny inside.
Results (from left to right, canny has all lines, in preview there is some compression issue):

How to fill OpenCV image with one solid color?

How to fill OpenCV image with one solid color?
Using the OpenCV C API with IplImage* img:
Use cvSet(): cvSet(img, CV_RGB(redVal,greenVal,blueVal));
Using the OpenCV C++ API with cv::Mat img, then use either:
cv::Mat::operator=(const Scalar& s) as in:
img = cv::Scalar(redVal,greenVal,blueVal);
or the more general, mask supporting, cv::Mat::setTo():
img.setTo(cv::Scalar(redVal,greenVal,blueVal));
Here's how to do with cv2 in Python:
# Create a blank 300x300 black image
image = np.zeros((300, 300, 3), np.uint8)
# Fill image with red color(set each pixel to red)
image[:] = (0, 0, 255)
Here's more complete example how to create new blank image filled with a certain RGB color
import cv2
import numpy as np
def create_blank(width, height, rgb_color=(0, 0, 0)):
"""Create new image(numpy array) filled with certain color in RGB"""
# Create black blank image
image = np.zeros((height, width, 3), np.uint8)
# Since OpenCV uses BGR, convert the color first
color = tuple(reversed(rgb_color))
# Fill image with color
image[:] = color
return image
# Create new blank 300x300 red image
width, height = 300, 300
red = (255, 0, 0)
image = create_blank(width, height, rgb_color=red)
cv2.imwrite('red.jpg', image)
Create a new 640x480 image and fill it with purple (red+blue):
cv::Mat mat(480, 640, CV_8UC3, cv::Scalar(255,0,255));
Note:
height before width
type CV_8UC3 means 8-bit unsigned int, 3 channels
colour format is BGR
The simplest is using the OpenCV Mat class:
img=cv::Scalar(blue_value, green_value, red_value);
where img was defined as a cv::Mat.
Use numpy.full. Here's a Python that creates a gray, blue, green and red image and shows in a 2x2 grid.
import cv2
import numpy as np
gray_img = np.full((100, 100, 3), 127, np.uint8)
blue_img = np.full((100, 100, 3), 0, np.uint8)
green_img = np.full((100, 100, 3), 0, np.uint8)
red_img = np.full((100, 100, 3), 0, np.uint8)
full_layer = np.full((100, 100), 255, np.uint8)
# OpenCV goes in blue, green, red order
blue_img[:, :, 0] = full_layer
green_img[:, :, 1] = full_layer
red_img[:, :, 2] = full_layer
cv2.imshow('2x2_grid', np.vstack([
np.hstack([gray_img, blue_img]),
np.hstack([green_img, red_img])
]))
cv2.waitKey(0)
cv2.destroyWindow('2x2_grid')
For an 8-bit (CV_8U) OpenCV image, the syntax is:
Mat img(Mat(nHeight, nWidth, CV_8U);
img = cv::Scalar(50); // or the desired uint8_t value from 0-255
color=(200, 100, 255) # sample of a color
img = np.full((100, 100, 3), color, np.uint8)
If you are using Java for OpenCV, then you can use the following code.
Mat img = src.clone(); //Clone from the original image
img.setTo(new Scalar(255,255,255)); //This sets the whole image to white, it is R,G,B value
I personally made this python code to change the color of a whole image opened or created with openCV . I am sorry if it's not good enough , I am beginner 😚😚 .
def OpenCvImgColorChanger(img,blue = 0,green = 0,red = 0):
line = 1
ImgColumn = int(img.shape[0])-2
ImgRaw = int(img.shape[1])-2
for j in range(ImgColumn):
for i in range(ImgRaw):
if i == ImgRaw-1:
line +=1
img[line][i][2] = int(red)
img[line][i][1] = int(green)
img[line][i][0] = int(blue)

Resources