pytesseract Erase table borders as delicately as possible - python

I am using pytesseract and opencv to erase the borders of an image.
to extract text from image using tesseract
This is the source code I wrote based on this post.
What's the way to remove all lines and borders in image(keep texts) programmatically?
import cv2
import os
try:
from PIL import Image
except ImportError:
import Image
import pytesseract
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract'
image = cv2.imread("D://ocrtestimg//id.jpg")
result = image.copy()
gray = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (30,1))
remove_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(remove_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 5) # contours 그리기
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,30))
remove_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(remove_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 5)
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.imwrite('result.png', result)
cv2.waitKey()
image = cv2.imread('result.png')
result = image.copy()
gray = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
filename = "{}.png".format(os.getpid())
print(filename)
cv2.imwrite(filename, gray)
#Simple image to string- OCR
text = pytesseract.image_to_string(Image.open(filename), lang='eng')
os.remove(filename)
print(text)
cv2.imshow("Image", image)
cv2.waitKey(0)
image with borders removed
The image quality is poor, so the vertical lines in the table are broken.
What additional work do I need to do to erase this broken line?
Even if you increase the dpi by importing an image or increase the size of the photo, the broken vertical lines are not erased.

Related

How to separate each rectangle tiles and crop it from below image of video conferencing?

The image has 4 streams, how do we crop each stream, Disclaimer: The number of streams could be 4, 8, 16, 32
I have tried to develop an algorithm with the below steps.
Get horizontal and vertical lines
Draw on the line.
Find rectangle tiles using the find contours method from OpenCV
Crop each stream and save it into a different file.
But this approach does not hold true for a random number of streams.
Let me know your inputs on this.
import cv2
# Load image, convert to grayscale, Otsu's threshold
image = cv2.imread(r'C:\oldLaptopData\PF_2021\iotG_work\test_black\MSBlack.jpg')
#image = cv2.imread(r"C:\oldLaptopData\PF_2021\iotG_data\video_samples\split_frames\out1.jpg")
result = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Detect horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (200, 1))
detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
print(len(cnts))
for c in cnts:
cv2.drawContours(result, [c], -1, (36,255,12), 10)
# Detect vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 200))
detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (36,255,12), 10)
cv2.imwrite('result.jpg', result)

How to highlight unusual marks on mobile devices

I'm working on sample task where I need to highlight unusual marks on any mobile devices. I'm trying with opencv python. But, I'm not getting actual contors for the unusual marks.
Input image is like:
And output image is expected as below:
I'm trying something like below, but it didn't work.
import cv2
from matplotlib import pyplot as plt
blurValue = 15
img_path = "input.jpg"
# reading the image
image = cv2.imread(img_path)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (blurValue, blurValue), 0)
edged = cv2.Canny(image, 100, 255)
#applying closing function
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
closed = cv2.morphologyEx(edged, cv2.MORPH_CLOSE, kernel)
lower = np.array([4, 20, 93])
upper = np.array([83, 79, 166])
# hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# blur = cv2.GaussianBlur(hsv, (blurValue, blurValue), 0)
mask = cv2.inRange(closed, lower, upper)
result_1 = cv2.bitwise_and(frame, frame, mask = mask)
cnts = cv2.findContours(result_1.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
cv2.drawContours(image, [approx], -1, (0, 255, 0), 2)
plt.imshow(image)
plt.title("image")
plt.show()
Any help will be appreciated. Thank you.
My suggestion would be to use adaptive thresholding and filter on area (and possibly other characteristics). Here is my code and results using Python OpenCV.
Input:
import cv2
import numpy as np
# read image
img = cv2.imread("iphone.jpg")
# convert img to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# apply gaussian blur
blur = cv2.GaussianBlur(gray, (29,29), 0)
# do adaptive threshold on gray image
thresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 51, 3)
# apply morphology open then close
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (17, 17))
open = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
close = cv2.morphologyEx(open, cv2.MORPH_CLOSE, kernel)
# Get contours
cnts = cv2.findContours(close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
result = img.copy()
for c in cnts:
area = cv2.contourArea(c)
if area < 10000 and area > 5000:
cv2.drawContours(result, [c], -1, (0, 255, 0), 2)
# write results to disk
cv2.imwrite("iphone_thresh.jpg", thresh)
cv2.imwrite("iphone_close.jpg", close)
cv2.imwrite("iphone_markings.jpg", result)
# display it
cv2.imshow("IMAGE", img)
cv2.imshow("THRESHOLD", thresh)
cv2.imshow("CLOSED", close)
cv2.imshow("RESULT", result)
cv2.waitKey(0)
Thresholded Image:
Morphology Processed Image:
Final Result:
I would also suggest that you align the image with a known clean iPhone image and create a mask of the camera and logo, etc., markings so that you can filter the results to exclude those (and perhaps even the border of the camera outline).

Extract handwritten characters from a boxed form field image

I am trying to extract handwritten characters from field boxes
My desired output would be the character segments with the boxes removed. So far, I've tried defining contours and filtering by area but that hasn't yielded any good results.
# Reading image and binarization
im = cv2.imread('test.png')
char_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
char_bw = cv2.adaptiveThreshold(char_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 75, 10)
# Applying erosion and dilation
kernel = np.ones((5,5), np.uint8)
img_erosion = cv2.erode(char_bw, kernel, iterations=1)
img_dilation = cv2.dilate(img_erosion, kernel, iterations=1)
# Find Canny edges
edged = cv2.Canny(img_dilation, 100, 200)
# Finding Contours
edged_copy = edged.copy()
im2, cnts, hierarchy = cv2.findContours(edged_copy, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print("Number of Contours found = " + str(len(cnts)))
# Draw all contours
cv2.drawContours(im, cnts, -1, (0, 255, 0), 3)
# Filter using area and save
for no, c in enumerate(cnts):
area = cv2.contourArea(c)
if area > 100:
contour = c
(x, y, w, h) = cv2.boundingRect(contour)
img = im[y:y+h, x:x+w]
cv2.imwrite(f'./cnts/cnt-{no}.png', img_dilation)
Here's a simple approach:
Obtain binary image. We load the image, enlarge using imutils.resize(), convert to grayscale, and perform Otsu's thresholding to obtain a binary image
Remove horizontal lines. We create a horizontal kernel then perform morphological opening and remove the horizontal lines using cv2.drawContours
Remove vertical lines. We create a vertical kernel then perform morphological opening and remove the vertical lines using cv2.drawContours
Here's a visualization of each step:
Binary image
Detected lines/boxes to remove highlighted in green
Result
Code
import cv2
import numpy as np
import imutils
# Load image, enlarge, convert to grayscale, Otsu's threshold
image = cv2.imread('1.png')
image = imutils.resize(image, width=500)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Remove horizontal
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (25,1))
detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(image, [c], -1, (255,255,255), 5)
# Remove vertical
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,25))
detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(image, [c], -1, (255,255,255), 5)
cv2.imshow('thresh', thresh)
cv2.imshow('image', image)
cv2.waitKey()

Identify table grid in image

I have to identify the table grid in this image and change it to Grimson red color. I am a beginner in image processing.
img_arr = mpimg.imread("1.jpg")
plt.imshow(img_arr)
grid = img_arr[470:800,42:670,(0,1,2)]
plt.imshow(grid.data)
Based on the image dimensions I was able to see the grid part of the image but I don't have idea how to identify the grid and change its color. If anyone has any idea about this, please reply.
Here's an approach:
Convert image to grayscale and threshold
Find contours and filter using contour area to isolate the grid
Find horizontal and vertical lines
Draw lines onto image
Here's the result
import cv2
import numpy as np
image = cv2.imread('1.png')
mask = np.zeros(image.shape, dtype=np.uint8)
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Detect only grid
cnts = cv2.findContours(thresh, 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 > 10000:
cv2.drawContours(mask, [c], -1, (255,255,255), -1)
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
mask = cv2.bitwise_and(mask, thresh)
# Find horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (55,1))
detect_horizontal = cv2.morphologyEx(mask, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(image, [c], -1, (0,0,255), 2)
# Find vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,25))
detect_vertical = cv2.morphologyEx(mask, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(image, [c], -1, (0,0,255), 2)
cv2.imshow('thresh', thresh)
cv2.imshow('mask', mask)
cv2.imshow('image', image)
cv2.waitKey()

Remove small vertical lines in between the character from an image

I want to remove all horizontal and vertical lines. I am able to remove horizontal lines, but while removing small vertical lines the original text is also getting impacted. This is the code I'm using:
image = cv2.imread('opt/doc/uploads/img1.png')
result = image.copy()
blur = image.copy()
gray = cv2.cvtColor(blur,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0,255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
rows,cols = thresh.shape
horizontalsize = int(cols // 30)
verticalsize = int(rows // 30)
# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (horizontalsize,1))
remove_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel)
cnts = cv2.findContours(remove_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 3)
# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,20))
remove_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel)
cnts = cv2.findContours(remove_vertical, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255),3)
cv2.imwrite('result.png', result)
PFB 2 the input image:
PFB output Image of above 2 images respectively:
Instead of trying to detect horizontal/vertical lines, another approach is to filter using contour area to "ignore" the lines and just take the desired text characters. One limitation is it will not detect text that is connected to the horizontal/vertical lines
import cv2
import numpy as np
image = cv2.imread('1.png')
mask = np.ones(image.shape, dtype=np.uint8) * 255
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
cnts = cv2.findContours(thresh, 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 < 1000:
x,y,w,h = cv2.boundingRect(c)
mask[y:y+h, x:x+w] = image[y:y+h, x:x+w]
cv2.imshow('thresh', thresh)
cv2.imshow('mask', mask)
cv2.waitKey()

Categories