How to whiten background and blaken grid in a same image - opencv

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):

Related

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

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:

How to obtain long edge between black and white thresholds?

Using OpenCv I was converted green areas to white(255) and black(0): see CURRENT OUTPUT.
Using Canny, Laplacian and Sobel edge detection yielded many little edges, instead of a long consistent edge see CANNY EDGE Example.
How could I achieve one long edge as seen in the desired output?
Relevant Code:
image = cv.imread('grass pic.jpg')
lane_image = np.copy(image)
pic = cv.cvtColor(lane_image ,cv.COLOR_BGR2RGB)
lower = np.array([24,0,0])
upper = np.array([177, 194, 20])
green_selection = cv.inRange(pic, lower, upper)
canny= cv.Canny(green_selection,50,150)
plt.imshow(canny, cmap='gray')
CURRENT OUTPUT:
CANNY EDGE Example
DESIRED OUTPUT:
Converting the image to HSV colour space might help you to detect your desired line.
import cv2
import numpy as np
image = cv2.imread("image.png")
HSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
low = np.array([23, 98, 0])
high = np.array([253, 255, 255])
mask = cv2.inRange(HSV, low, high)
cv2.imshow("mask", mask)
cv2.imwrite("mask.png", mask)
result = cv2.bitwise_and(image, image, mask=mask)
cv2.imshow("result", result)
cv2.imwrite("result.png", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Mask Image
Result Image

Reading the characters from image - remove curve

I'm trying to read the characters from this image. But the curve in between is obtruding the process. Can someone help me with this please? How can I extract the letters from such images?
img = cv2.imread('screenshot.png', 0)
ret, thresh_img = cv2.threshold(img, 125, 255, cv2.THRESH_BINARY_INV)
cv2.imshow('grey image',thresh_img)
cv2.imwrite("result11.jpg", thresh_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
I have tried to convert it to gray scale and remove the wave noise. I am unable to do it. Please help me with. Newbie to OpenCV.
You can apply the following sequence:
1- Adaptive Threshold
2- Morphological Transformation
3- Bitwise operation
Step-1: Adaptive Threshold
Get different thresholds for different regions of the same image for better results.
Result:
Step-2: Morphological Transformation
We will apply:
erosion followed by dilation (opening)
dilation followed by erosion (closing)
Result:
Step-3: Bitwise operation
Useful for extracting part of the image
Result:
Now if we read from the image, result will be:
dbdhm
Code:
import cv2
import numpy as np
import pytesseract
img = cv2.imread("UzhPS.png")
gry = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
flt = cv2.adaptiveThreshold(gry,
100, cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 13, 16)
krn = np.ones((3, 3), np.uint8)
opn = cv2.morphologyEx(flt, cv2.MORPH_OPEN, krn)
cls = cv2.morphologyEx(opn, cv2.MORPH_CLOSE, krn)
gry = cv2.bitwise_or(gry, cls)
txt = pytesseract.image_to_string(gry)
print(txt[2:])

Processing an image with block letters for OCR

I would like to OCR this battle log from a game:
battle log image
The original image had block text font, so after thresholding I inverted the colors. Now I want to remove the "black" background (but not the black text), but I'm not sure how to achieve that in OpenCV. Afterwards I imagine I would like to sharpen the text to be thicker for better OCR.
May I ask how I can go about this?
Try this out.
import cv2
import numpy as np
img = cv2.imread("1.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
invert0 = cv2.bitwise_not(gray)
_,thresh = cv2.threshold(invert0,128,255,cv2.THRESH_BINARY)
invert1 = cv2.bitwise_not(thresh)
im2, contours, hierarchy = cv2.findContours(invert1,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
mask = np.zeros(gray.shape, dtype="uint8")
for i in range(len(contours)):
if(hierarchy[0][i][3]==-1): #contour has no parent (most outer contour)
cv2.fillPoly(mask, pts =[contours[i]], color=255)
invert2 = cv2.bitwise_not(mask)
res = invert2 + invert1
cv2.imshow("img", img)
cv2.imshow("gray", gray)
cv2.imshow("invert0", invert0)
cv2.imshow("thresh", thresh)
cv2.imshow("invert1", invert1)
cv2.imshow("invert2", invert2)
cv2.imshow("mask", mask)
cv2.imshow("res", res)
cv2.waitKey()
cv2.destroyAllWindows()

OpenCV - Extracting lines on a graph

I would like to create a program that is able to extract lines from a graph.
For example, if a graph like this is inputted, I would just want the red line to be outputted.
Below I have tried to do this using a hough line transformation, however, I do not get very promising results.
import cv2
import numpy as np
graph_img = cv2.imread("/Users/2020shatgiskessell/Desktop/Graph1.png")
gray = cv2.cvtColor(graph_img, cv2.COLOR_BGR2GRAY)
kernel_size = 5
#grayscale image
blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)
#Canny edge detecion
edges = cv2.Canny(blur_gray, 50, 150)
#Hough Lines Transformation
#distance resoltion of hough grid (pixels)
rho = 1
#angular resolution of hough grid (radians)
theta = np.pi/180
#minimum number of votes
threshold = 15
#play around with these
min_line_length = 25
max_line_gap = 20
#make new image
line_image = np.copy(graph_img)
#returns array of lines
lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),
min_line_length, max_line_gap)
for line in lines:
for x1,y1,x2,y2 in line:
cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),2)
lines_edges = cv2.addWeighted(graph_img, 0.8, line_image, 1, 0)
cv2.imshow("denoised image",edges)
if cv2.waitKey(0) & 0xff == 27:
cv2.destroyAllWindows()
This produces the output image below, which does not accurately recognize the graph line. How might I go about doing this?
Note: For now, I am not concerned about the graph titles or any other text.
I would also like the code to work for other graph images aswell, such as:
etc.
If the graph does not have many noises around it (like your example) I would suggest to threshold your image with Otsu threshold instead of looking for edges . Then you simply search the contours, select the biggest one (graph) and draw it on a blank mask. After that you can perform a bitwise operation on image with the mask and you will get a black image with the graph. If you like the white background better, then simply change all black pixels to white. Steps are written in the example. Hope it helps a bit. Cheers!
Example:
import numpy as np
import cv2
# Read the image and create a blank mask
img = cv2.imread('graph.png')
h,w = img.shape[:2]
mask = np.zeros((h,w), np.uint8)
# Transform to gray colorspace and threshold the image
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# Search for contours and select the biggest one and draw it on mask
_, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)
cv2.drawContours(mask, [cnt], 0, 255, -1)
# Perform a bitwise operation
res = cv2.bitwise_and(img, img, mask=mask)
# Convert black pixels back to white
black = np.where(res==0)
res[black[0], black[1], :] = [255, 255, 255]
# Display the image
cv2.imshow('img', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
EDIT:
For noisier pictures you could try this code. Note that different graphs have different noises and may not work on every graph image since the denoisiation process would be specific in every case. For different noises you can use different ways to denoise it, for example histogram equalization, eroding, blurring etc. This code works well for all 3 graphs. Steps are written in comments. Hope it helps. Cheers!
import numpy as np
import cv2
# Read the image and create a blank mask
img = cv2.imread('graph.png')
h,w = img.shape[:2]
mask = np.zeros((h,w), np.uint8)
# Transform to gray colorspace and threshold the image
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# Perform opening on the thresholded image (erosion followed by dilation)
kernel = np.ones((2,2),np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
# Search for contours and select the biggest one and draw it on mask
_, contours, hierarchy = cv2.findContours(opening,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)
cv2.drawContours(mask, [cnt], 0, 255, -1)
# Perform a bitwise operation
res = cv2.bitwise_and(img, img, mask=mask)
# Threshold the image again
gray = cv2.cvtColor(res,cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# Find all non white pixels
non_zero = cv2.findNonZero(thresh)
# Transform all other pixels in non_white to white
for i in range(0, len(non_zero)):
first_x = non_zero[i][0][0]
first_y = non_zero[i][0][1]
first = res[first_y, first_x]
res[first_y, first_x] = 255
# Display the image
cv2.imshow('img', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:

Resources