CV2 not detecting table cells' bounding boxes - python

I'm making a program that should convert an image of a table into a csv file.
I'm getting along with cv2 and tesseract but I don't get why my code (using findContours()) find a huge lot of contours but no the cells' ones, contours around the text inside.
Here is my code :
import os
import sys
import cv2
import pytesseract
from pytesseract import Output
import preprocessing as pre
import pandas as pd
def show_img(img):
contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
print(contours)
for contour in contours:
(x, y, w, h) = cv2.boundingRect(contour)
cv2.circle(img, (x, y), 5, (0, 255, 0), -1)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
def convert(image_path):
# Preprocess the image
img = pre.processImg(image_path)
show_img(img)
csv = make_csv(img)
return csv
if(len(sys.argv) < 2):
print("usage: python3 img2csv.py <image_path>")
exit()
image_path = sys.argv[1]
csv = convert(image_path)
Here is a screenshot of my image without contours :
And here is a screenshot of the res :
So as you can see, it looks like my code draws the borders of the texts and not the cells.
Thanks for your help !
Edit :
With a different radius for the circles it looks like this :

Related

is there a way of converting PDF file to png bytes without using pdf2image

What I'm trying to achieve is to compare two images (engineering drawings as pdf) by uploading them using Streamlit 'st.file_uploader()', the code here works well but how can I do the same without Poppler...
import streamlit as st
import imutils
import cv2
import numpy as np
import io
from pdf2image import convert_from_bytes
from io import BytesIO
from PIL import Image
def pdf_to_png_bytes(pdfFile):
pdfFile = BytesIO(pdfFile.read())
pdfFile.seek(0)
imgFile = convert_from_bytes(pdfFile.read(), poppler_path=r"poppler\bin")
buffer = io.BytesIO()
imgFile[0].save(buffer, 'png')
buffer = buffer.getvalue()
return buffer
After spending several days trying to find an alternative solution, I couldn't do the same the steps without using pdf2image and Poppler components... The reason is that I cannot have Poppler emulated in the docker file of my company..
After a get the png byte string from 'pdf_to_png_bytes', I use the function bellow to compare both images:
def calculate_image_diff(img1, img2):
original = cv2.imdecode(np.frombuffer(img1, np.uint8), -1)
new = cv2.imdecode(np.frombuffer(img2, np.uint8), -1)
diff = original.copy()
cv2.absdiff(original, new, diff)
gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)
for i in range(0, 3):
dilated = cv2.dilate(gray.copy(), None, iterations = i + 1)
(T, thresh) = cv2.threshold(dilated, 0, 255, cv2.THRESH_BINARY)
cnts = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
for c in cnts:
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(new, (x, y), (x + w, y + h), (0, 255, 0), 2)
new = Image.fromarray(new)
return new
Like I said, this code works perfectly... but I need to find an alternative without using Poppler. Can you help me?

How can I count number of stomata in the microscopic image of leaf using Python and Opencv?

I want to count the number of stomata in the microscopic images of leaves. One of the samples is attached below. The stomata is characterized by oval shape in general with thick black lining in between. Since I have many images, I want to automate the process. I am familiar with Python but quite novice to computer vision.
With some code, I was able to count the number of cars in an image. However, it did not work with the image of the leaf for me. The sample code is given below:
import cv2
import numpy as np
import matplotlib.pyplot as plt
import cvlib as cv
from cvlib.object_detection import draw_bbox
from numpy.lib.polynomial import poly
image = cv2.imread("leaf1.jpg")
box, label, count = cv.detect_common_objects(image)
output = draw_bbox(image, box, label, count)
plt.imshow(output)
plt.show()
I got the results as follows with no detection of any object in the image. Is it possible to count the number of stomata in the leaf images as these?
I hope I understood what you mean by the word stomata :)
import sys
import cv2
import numpy as np
# Load image
pth = sys.path[0]
im = cv2.imread(pth+'/stomata.jpg')
im = cv2.resize(im, (im.shape[1]//2, im.shape[0]//2))
h, w = im.shape[:2]
# Remove noise and make grayscale
blr = cv2.medianBlur(im, 11)
blr = cv2.cvtColor(blr, cv2.COLOR_BGR2GRAY)
# Remove noise again
bw = cv2.medianBlur(blr, 45) # 51
bw = cv2.erode(bw, np.ones((5, 5), np.uint8))
# Create mask
bw = cv2.threshold(bw, 110, 255, cv2.THRESH_BINARY)[1]
# Draw white border around mask to detect stomatas near borders
cv2.rectangle(bw, (0, 0), (w, h), 255, 5)
# Find contours and sort them by position
cnts, _ = cv2.findContours(bw, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnts.sort(key=lambda x: cv2.boundingRect(x)[0])
# Find and draw blocks
for cnt in cnts:
x, y, w, h = cv2.boundingRect(cnt)
cv2.rectangle(im, (x, y), (x+w, y+h), (0, 255, 0), 5)
cv2.rectangle(bw, (x, y), (x+w, y+h), 127, 5)
# Save final output
bw = cv2.cvtColor(bw, cv2.COLOR_GRAY2BGR)
cv2.imwrite(pth+'/stack.jpg', np.hstack((im, bw)))
This is a start; It is not complete and has an error in detection. You may need more images to get better results. You need to take the time to get the desired result. I'm not sure about the next sentence, but you may need to use something like CNN(ConvNet) later.

How to detect lane from an image using opencv and python3

I am new to Image Processing and am trying to detect side lanes in a given image.
This is the Expected output:
I need an output something similar to this
So i thought of using houghTransform to detect the straight lane lines
but its providing me a large number of lines as output.
Finally i was able to filter out it to few lines as image below
This is the output obtained :
AM sort of stuck now and have no idea how to proceed. I have searched online in many sites but none gave me the result i needed.
import numpy as np
import cv2
img = cv2.imread('frame3.jpg')
print(img.shape)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,100,250,apertureSize = 3)
print(img.shape[1])
print(img.shape)
minLineLength=img.shape[1]-300
lines = cv2.HoughLinesP(image=edges,rho=0.02,theta=np.pi/500,
threshold=1,lines=np.array([]), minLineLength=minLineLength,maxLineGap=100)
a,b,c = lines.shape
for i in range(a):
cv2.line(img, (lines[i][0][0], lines[i][0][1]), (lines[i][0][2],
lines[i][0][3]), (0, 0, 255), 3, cv2.LINE_AA)
cv2.imshow('edges', edges)
cv2.imshow('result', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Is there a function similar to OpenCV findContours that detects curves and replaces points with a spline?

I am trying to take the below image, trace the white shape, and export the resulting path to pdf. The problem I have is that findContours seeming only finds points along the edge of the shape. Is there a solution out there, similar to findContours, that detects curves in a shape and replaces its points with a spline wherever there is a curve? If I use scipy.interpolate it ignores straight lines and turns the entire contour into one big curved shape, which is no good either. I need something that does both things.
import numpy as np
import cv2
from scipy.interpolate import splprep, splev
from pyx import *
import matplotlib.pyplot as plt
#read in image file
original = cv2.imread('test.jpg')
#blur the image to smooth edges
im = cv2.medianBlur(original,5)
#threshold the image
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,170,255,cv2.THRESH_BINARY)
#findContours
im2, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_\
APPROX_SIMPLE)
#drawContours
cv2.drawContours(original, [approx], -1, (0,255,0), 3)
cv2.imshow("Imageee", original)
cv2.waitKey(0)
Except using cv2.findContours with flag cv2.CHAIN_APPROX_SIMPLE to approx the contours, we can do it manually.
use cv2.findContours with flag cv2.CHAIN_APPROX_NONE to find contours.
use cv2.arcLength to calculate the contour length.
use cv2.approxPoolyDP to approx the contour manually with epsilon = eps * arclen.
Here is one of the results when eps=0.005:
More results:
#!/usr/bin/python3
# 2018.01.04 13:01:24 CST
# 2018.01.04 14:42:58 CST
import cv2
import numpy as np
import os
img = cv2.imread("test.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret,threshed = cv2.threshold(gray,170,255,cv2.THRESH_BINARY)
# find contours without approx
cnts = cv2.findContours(threshed,cv2.RETR_LIST,cv2.CHAIN_APPROX_NONE)[-2]
# get the max-area contour
cnt = sorted(cnts, key=cv2.contourArea)[-1]
# calc arclentgh
arclen = cv2.arcLength(cnt, True)
# do approx
eps = 0.0005
epsilon = arclen * eps
approx = cv2.approxPolyDP(cnt, epsilon, True)
# draw the result
canvas = img.copy()
for pt in approx:
cv2.circle(canvas, (pt[0][0], pt[0][1]), 7, (0,255,0), -1)
cv2.drawContours(canvas, [approx], -1, (0,0,255), 2, cv2.LINE_AA)
# save
cv2.imwrite("result.png", canvas)
I think your problem actually consists of two issues.
The first issue is to extract the contour, which you can achieve using teh findContour function:
import numpy as np
print cv2.__version__
rMaskgray = cv2.imread('test.jpg', 0)
(thresh, binRed) = cv2.threshold(rMaskgray, 200, 255, cv2.THRESH_BINARY)
_, Rcontours, hier_r = cv2.findContours(binRed,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
r_areas = [cv2.contourArea(c) for c in Rcontours]
max_rarea = np.argmax(r_areas)
CntExternalMask = np.ones(binRed.shape[:2], dtype="uint8") * 255
contour= Rcontours[max_rarea]
cv2.drawContours(CntExternalMask,[contour],-1,0,1)
print "These are the contour points:"
print c
print
print "shape: ", c.shape
for p in contour:
print p[0][0]
cv2.circle(CntExternalMask, (p[0][0], p[0][1]), 5, (0,255,0), -1)
cv2.imwrite("contour.jpg", CntExternalMask)
cv2.imshow("Contour image", CntExternalMask)
cv2.waitKey(0)
If you execute the program, the contour points are printed as a list of point coordinates.
The contour approximation method you choose influences the interpolation which is actually used (and the number of points found), as described here. I have added small dots at the points found with the approximation method cv2.CHAIN_APPROX_SIMPLE. You see that the straight lines are already approximated.
I may not fully have understood your second step, though. You want to omit some of those points, replacing point lists partially by splines. There might be different way to do this, depending on your final intention. Do you just want to replace the straight lines? If you replace curved parts, what is the margin of error you are allowing?
# import the necessary packages
import numpy as np
import argparse
import glob
import cv2
#For saving pdf
def save_pdf(imagename):
import img2pdf
# opening from filename
with open("output.pdf","wb") as f:
f.write(img2pdf.convert(imagename))
#for fouind biggest contours
def bigercnt(contours):
max_area=0
cnt=[]
for ii in contours:
area=cv2.contourArea(ii)
if area>max_area:
cnt = ii
return cnt
#STARTING
print ("Reading img.jpg file")
# load the image, convert it to grayscale, and blur it slightly
image = cv2.imread('img.jpg')
image = cv2.resize(image, (0,0), fx=0.5, fy=0.5)
print ("Converting it gray scale")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
print ("Bluring")
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
print ("Looking for edges" )
# apply Canny edge detection using a wide threshold, tight
# threshold, and automatically determined threshold
tight = cv2.Canny(blurred, 255, 250)
print ("Looking for contours")
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))
close = cv2.morphologyEx(tight, cv2.MORPH_CLOSE, kernel)
_,contours, hierarchy = cv2.findContours( close.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print("Looking for big contour")
cnt = bigercnt(contours)
print ("Cropping found contour")
x,y,w,h = cv2.boundingRect(cnt)
croped_image = image[y:y+h,x:x+w]
img2 = np.zeros((h,w,4),np.uint8)
print ("Taking only pixels in countour and creating png")
for i in range(h):
for j in range(w):
#print (x+j, y+i)
#print cv2.pointPolygonTest(cnt, (x+j, y+i), False)
if cv2.pointPolygonTest(cnt, (x+j, y+i), False)==1:
#print True
img2[i,j] = [croped_image[i, j][0],croped_image[i, j][1],croped_image[i, j][2],255]
else:
img2[i,j] = [255,255,255,0]
print ("Showing output image")
# Show the output image
#cv2.imshow('croped', croped_image)
cv2.imshow('output', img2)
params = list()
params.append(cv2.IMWRITE_PNG_COMPRESSION)
params.append(8)
print ("Saving output image")
cv2.imwrite("output.png",img2,params)
print ("Finish:converted")
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV MSER detect text areas - Python

I have an invoice image, and I want to detect the text on it. So I plan to use 2 steps: first is to identify the text areas, and then using OCR to recognize the text.
I am using OpenCV 3.0 in python for that. I am able to identify the text(including some non text areas) but I further want to identify text boxes from the image(also excluding the non-text areas).
My input image is:
And the output is:
I am using the below code for this:
img = cv2.imread('/home/mis/Text_Recognition/bill.jpg')
mser = cv2.MSER_create()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #Converting to GrayScale
gray_img = img.copy()
regions = mser.detectRegions(gray, None)
hulls = [cv2.convexHull(p.reshape(-1, 1, 2)) for p in regions]
cv2.polylines(gray_img, hulls, 1, (0, 0, 255), 2)
cv2.imwrite('/home/mis/Text_Recognition/amit.jpg', gray_img) #Saving
Now, I want to identify the text boxes, and remove/unidentify any non-text areas on the invoice. I am new to OpenCV and am a beginner in Python. I am able to find some examples in MATAB example and C++ example, but If I convert them to python, it will take a lot of time for me.
Is there any example with python using OpenCV, or can anyone help me with this?
Below is the code
# Import packages
import cv2
import numpy as np
#Create MSER object
mser = cv2.MSER_create()
#Your image path i-e receipt path
img = cv2.imread('/home/rafiullah/PycharmProjects/python-ocr-master/receipts/73.jpg')
#Convert to gray scale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
vis = img.copy()
#detect regions in gray scale image
regions, _ = mser.detectRegions(gray)
hulls = [cv2.convexHull(p.reshape(-1, 1, 2)) for p in regions]
cv2.polylines(vis, hulls, 1, (0, 255, 0))
cv2.imshow('img', vis)
cv2.waitKey(0)
mask = np.zeros((img.shape[0], img.shape[1], 1), dtype=np.uint8)
for contour in hulls:
cv2.drawContours(mask, [contour], -1, (255, 255, 255), -1)
#this is used to find only text regions, remaining are ignored
text_only = cv2.bitwise_and(img, img, mask=mask)
cv2.imshow("text only", text_only)
cv2.waitKey(0)
This is an old post, yet I'd like to contribute that if you are trying to extract all the texts out of an image, here is the code to get that text in an array.
import cv2
import numpy as np
import re
import pytesseract
from pytesseract import image_to_string
pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"
from PIL import Image
image_obj = Image.open("screenshot.png")
rgb = cv2.imread('screenshot.png')
small = cv2.cvtColor(rgb, cv2.COLOR_BGR2GRAY)
#threshold the image
_, bw = cv2.threshold(small, 0.0, 255.0, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
# get horizontal mask of large size since text are horizontal components
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (20, 1))
connected = cv2.morphologyEx(bw, cv2.MORPH_CLOSE, kernel)
# find all the contours
contours, hierarchy,=cv2.findContours(connected.copy(),cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
#Segment the text lines
counter=0
array_of_texts=[]
for idx in range(len(contours)):
x, y, w, h = cv2.boundingRect(contours[idx])
cropped_image = image_obj.crop((x-10, y, x+w+10, y+h ))
str_store = re.sub(r'([^\s\w]|_)+', '', image_to_string(cropped_image))
array_of_texts.append(str_store)
counter+=1
print(array_of_texts)

Categories