Combining 2 tresholded images to get this effect - python

I have the following 2 images:
How could I combine the images to get any of these 2 images?
My code:
import cv2
import numpy as np
image = cv2.imread('skadi.png')
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
cv2.imshow('greyscale',gray)
_, binary = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY)
cv2.imshow('treshold',binary)
binary= 255 - binary
cv2.imshow('inverted',binary)
kernel = np.ones((25, 25), np.uint8)
closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
cv2.imshow('closing',closing)
#closing = 255-closing
closing2 = cv2.bitwise_not(closing)
cv2.imshow('invetedclosing',closing2)
result = cv2.bitwise_or(closing, closing2)
cv2.imshow('convned',result)
edges = cv2.Canny(result, 100, 200)
cv2.waitKey(0)
cv2.destroyAllWindows()
I tried combining them wit cv2.bitwise_or and cv2.bitwise_xor, but ended with a white screen.
Any help appreciated!

Here's a handy script that basically extracts the biggest white blob in a binary image. Since the biggest white blob in your image is blob is the foreground (and the shape you are looking for), this should give you the expected result.
It basically gets all the external contours and keeps the contour with the biggest area. It then draws it on a new image.
This is the code, I'm using this image, since you did not provide the original.
# Imports:
import cv2
import numpy as np
# image path
path = "D://opencvImages//"
# Reading an image in default mode:
inputImage = cv2.imread(path + "testBlob.png")
# Grayscale conversion:
grayscaleImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)
# Threshold via Otsu:
# Note the image inversion:
_, binaryImage = cv2.threshold(grayscaleImage, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# Store a copy of the input image:
biggestBlob = binaryImage.copy()
# Set initial values for the
# largest contour:
largestArea = 0
largestContourIndex = 0
# Find the contours on the binary image:
contours, hierarchy = cv2.findContours(binaryImage, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
# Get the largest contour in the contours list:
for i, cc in enumerate(contours):
# Find the area of the contour:
area = cv2.contourArea(cc)
# Store the index of the largest contour:
if area > largestArea:
largestArea = area
largestContourIndex = i
# Once we get the biggest blob, paint it black:
tempMat = binaryImage.copy()
cv2.drawContours(tempMat, contours, largestContourIndex, (0, 0, 0), -1, 8, hierarchy)
# Erase smaller blobs:
biggestBlob = biggestBlob - tempMat
# Show the result:
cv2.imshow("biggestBlob", biggestBlob)
cv2.waitKey(0)
This is the result:

Related

Check if the rectangle contour contains numbers inside or not? (OpenCV - Python)

I know the quality is so so bad, but that's original image
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
rectKern = cv2.getStructuringElement(cv2.MORPH_RECT, (85, 64))
blackhat = cv2.morphologyEx(gray, cv2.MORPH_BLACKHAT, rectKern)
edges = cv2.Canny(light, 120, 255, 1)
squareKern = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
light = cv2.morphologyEx(gray, cv2.MORPH_OPEN, squareKern)
light = cv2.threshold(light, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
Here is the result
How can I check if inside that blue rectangle, there are numbers or not? (if there is no number inside, then I won't draw a bounding box around it since it's not license plate).
There are multiple methods for this. Choose depending on your requirement.
1- OCR via pytesseract - crop the rectangular region and then pass it to the tesseract to extract the text from the image.
# Import required packages
import cv2
import pytesseract
# Mention the installed location of Tesseract-OCR in your system
pytesseract.pytesseract.tesseract_cmd = 'System_path_to_tesseract.exe'
# Read image from which text needs to be extracted
img = cv2.imread("sample.jpg")
# Preprocessing the image starts
# Convert the image to gray scale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Performing OTSU threshold
ret, thresh1 = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY_INV)
# Specify structure shape and kernel size.
# Kernel size increases or decreases the area
# of the rectangle to be detected.
# A smaller value like (10, 10) will detect
# each word instead of a sentence.
rect_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (18, 18))
# Appplying dilation on the threshold image
dilation = cv2.dilate(thresh1, rect_kernel, iterations = 1)
# Finding contours
contours, hierarchy = cv2.findContours(dilation, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_NONE)
# Creating a copy of image
im2 = img.copy()
# A text file is created and flushed
file = open("recognized.txt", "w+")
file.write("")
file.close()
# Looping through the identified contours
# Then rectangular part is cropped and passed on
# to pytesseract for extracting text from it
# Extracted text is then written into the text file
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
# Drawing a rectangle on copied image
rect = cv2.rectangle(im2, (x, y), (x + w, y + h), (0, 255, 0), 2)
# Cropping the text block for giving input to OCR
cropped = im2[y:y + h, x:x + w]
# Open the file in append mode
file = open("recognized.txt", "a")
# Apply OCR on the cropped image
text = pytesseract.image_to_string(cropped)
# Appending the text into file
file.write(text)
file.write("\n")
# Close the file
file.close
Source: Link
2- opencv's EAST text detector - Tutorial
Also, have a look at this question for more methods
And this as well

How Can I Detect If There are Secondary Objects in an Image

I am looking for a way to detect if there are secondary objects in an image or if the image just has the one main object. I've done a bit of research, but I haven't been able to find anything quite like what I am looking for.
An example image would be:
The main object being the two detergent bottles since they overlap and the secondary object would be the "2 pack" pop up bubble in the top right. I would expect this image to return something like: "This image has secondary objects" or a count of the objects.
Here is one way to do that in Python/OpenCV
Read the input
Convert to gray and invert
OTSU threshold
Morphology close
Get external contours
Draw contours on image
Count contours
Print messages
Save results
Input:
import cv2
import numpy as np
# read image
img = cv2.imread("tide.jpg")
# convert img to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# invert gray image
gray = 255 - gray
# threshold gray image
#thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# apply morphology close
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# Get contours
cntrs = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]
result = img.copy()
for c in cntrs:
cv2.drawContours(result, [c], -1, (0,0,255), 1)
count = len(cntrs)
print("")
print("count =",count)
print("")
if count > 1:
print("This image has secondary objects")
else:
print("This image has primary object only")
# write results to disk
cv2.imwrite("tide_thresh.png", thresh)
cv2.imwrite("tide_morph.png", morph)
cv2.imwrite("tide_object_contours.png", result)
# display it
cv2.imshow("thresh", thresh)
cv2.imshow("morph", morph)
cv2.imshow("result", result)
cv2.waitKey(0)
Thresholded image:
Morphology close image:
Contours on image:
Count of contours and messages:
count = 2
This image has secondary objects
Following #fmw42's advice, I did a bit of research and found a script that worked well after a little bit of tinkering:
import cv2
import numpy as np
import sys
img = cv2.imread(sys.argv[1], cv2.IMREAD_UNCHANGED)
#convert img to grey
img_grey = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#set a thresh
thresh = 230
#get threshold image
ret,thresh_img = cv2.threshold(img_grey, thresh, 255, cv2.THRESH_BINARY_INV)
#find contours
contours, hierarchy = cv2.findContours(thresh_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#create an empty image for contours
# img_contours = np.zeros(img.shape)
img_contours = np.zeros_like(img)
# draw the contours on the empty image
cv2.drawContours(img_contours, contours, -1, 255, 3)
#save image
cv2.imshow('contours',img_contours)
# Wait indefinitely until you push a key. Once you do, close the windows
print len(contours)
cv2.waitKey(0)
cv2.destroyAllWindows()
My main issue was the threshold setting and I found that 230 worked best with my sample images, although it still is not perfect. I'm hoping there is a better way or something I can add to this.
this image returned 1 as expected, but my initial test image returns 3 at this threshold setting when I would expect 2. At 200 thresh it returns 2, but I was willing to compromise because the main thing I need to know is if it is more than 1.

Find contours based on edges

I want to detect the contours of an equipment label. Although the code runs correctly, it never quite detects the contours of the label.
Original Image
Using this code:
import numpy as np
import cv2
import imutils #resizeimage
import pytesseract # convert img to string
pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"
# Read the image file
image = cv2.imread('Car Images/5.JPG')
# Resize the image - change width to 500
image = imutils.resize(image, width=500)
# Display the original image
cv2.imshow("Original Image", image)
cv2.waitKey(0)
# RGB to Gray scale conversion
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow("1 - Grayscale Conversion", gray)
cv2.waitKey(0)
# Noise removal with iterative bilateral filter(removes noise while preserving edges)
gray = cv2.bilateralFilter(gray, 11, 17, 17)
cv2.imshow("2 - Bilateral Filter", gray)
cv2.waitKey(0)
# Find Edges of the grayscale image
edged = cv2.Canny(gray, 170, 200)
cv2.imshow("3 - Canny Edges", edged)
cv2.waitKey(0)
# Find contours based on Edges
cnts, new = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# Create copy of original image to draw all contours
img1 = image.copy()
cv2.drawContours(img1, cnts, -1, (0,255,0), 3)
cv2.imshow("4- All Contours", img1)
cv2.waitKey(0)
#sort contours based on their area keeping minimum required area as '30' (anything smaller than this will not be considered)
cnts=sorted(cnts, key = cv2.contourArea, reverse = True)[:30]
NumberPlateCnt = None #we currently have no Number plate contour
# Top 30 Contours
img2 = image.copy()
cv2.drawContours(img2, cnts, -1, (0,255,0), 3)
cv2.imshow("5- Top 30 Contours", img2)
cv2.waitKey(0)
# loop over our contours to find the best possible approximate contour of number plate
count = 0
idx =7
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
# print ("approx = ",approx)
if len(approx) == 4: # Select the contour with 4 corners
NumberPlateCnt = approx #This is our approx Number Plate Contour
# Crop those contours and store it in Cropped Images folder
x, y, w, h = cv2.boundingRect(c) #This will find out co-ord for plate
new_img = gray[y:y + h, x:x + w] #Create new image
cv2.imwrite('Cropped Images-Text/' + str(idx) + '.png', new_img) #Store new image
idx+=1
break
# Drawing the selected contour on the original image
#print(NumberPlateCnt)
cv2.drawContours(image, [NumberPlateCnt], -1, (0,255,0), 3)
cv2.imshow("Final Image With Number Plate Detected", image)
cv2.waitKey(0)
Cropped_img_loc = 'Cropped Images-Text/7.png'
cv2.imshow("Cropped Image ", cv2.imread(Cropped_img_loc))
# Use tesseract to covert image into string
text = pytesseract.image_to_string(Cropped_img_loc, lang='eng')
print("Equipment Number is :", text)
cv2.waitKey(0) #Wait for user input before closing the images displayed
Displayed output
Is there a better way to narrow down the contour to the equipment label?
Here is the code for your reference on github:
https://github.com/AjayAndData/Licence-plate-detection-and-recognition---using-openCV-only/blob/master/Car%20Number%20Plate%20Detection.py
I think this code may help you
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('C:/Users/DELL/Desktop/download (5).png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
corners = cv2.goodFeaturesToTrack(gray,60,0.001,10)
corners = np.int0(corners)
for i in corners:
x,y = i.ravel()
cv2.circle(img,(x,y),0,255,-1)
coord = np.where(np.all(img == (255, 0, 0),axis=-1))
plt.imshow(img)
plt.show()

How improve image quality to extract text from image using Tesseract

I'm trying to use Tessract in the code below to extract the two lines of the image. I tryied to improve the image quality but even though it didn't work.
Can anyone help me?
from PIL import Image, ImageEnhance, ImageFilter
import pytesseract
img = Image.open(r'C:\ocr\test00.jpg')
new_size = tuple(4*x for x in img.size)
img = img.resize(new_size, Image.ANTIALIAS)
img.save(r'C:\\test02.jpg', 'JPEG')
print( pytesseract.image_to_string( img ) )
Given the comment by #barny I don't know if this will work, but you can try the code below. I created a script that selects the display area and warps this into a straight image. Next a threshold to a black and white mask of the characters and the result is cleaned up a bit.
Try if it improves recognition. If it does, also look at the intermediate stages so you'll understand all that happens.
Update: It seems Tesseract prefers black text on white background, inverted and dilated the result.
Result:
Updated result:
Code:
import numpy as np
import cv2
# load image
image = cv2.imread('disp.jpg')
# create grayscale
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# perform threshold
retr, mask = cv2.threshold(gray_image, 190, 255, cv2.THRESH_BINARY)
# findcontours
ret, contours, hier = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# select the largest contour
largest_area = 0
for cnt in contours:
if cv2.contourArea(cnt) > largest_area:
cont = cnt
largest_area = cv2.contourArea(cnt)
# find the rectangle (and the cornerpoints of that rectangle) that surrounds the contours / photo
rect = cv2.minAreaRect(cont)
box = cv2.boxPoints(rect)
box = np.int0(box)
#### Warp image to square
# assign cornerpoints of the region of interest
pts1 = np.float32([box[2],box[3],box[1],box[0]])
# provide new coordinates of cornerpoints
pts2 = np.float32([[0,0],[500,0],[0,110],[500,110]])
# determine and apply transformationmatrix
M = cv2.getPerspectiveTransform(pts1,pts2)
tmp = cv2.warpPerspective(image,M,(500,110))
# create grayscale
gray_image2 = cv2.cvtColor(tmp, cv2.COLOR_BGR2GRAY)
# perform threshold
retr, mask2 = cv2.threshold(gray_image2, 160, 255, cv2.THRESH_BINARY_INV)
# remove noise / close gaps
kernel = np.ones((5,5),np.uint8)
result = cv2.morphologyEx(mask2, cv2.MORPH_CLOSE, kernel)
#draw rectangle on original image
cv2.drawContours(image, [box], 0, (255,0,0), 2)
# dilate result to make characters more solid
kernel2 = np.ones((3,3),np.uint8)
result = cv2.dilate(result,kernel2,iterations = 1)
#invert to get black text on white background
result = cv2.bitwise_not(result)
#show image
cv2.imshow("Result", result)
cv2.imshow("Image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV - findContours on generated image doesn't work

I have an image of a graph. I perform some preprocessing functions on the image in order to extract the graph line (which works). I then, however, try to find the contour of the graph line that is found and saved as a separate image. When I do this, however, I do not get the desired results.
Graph line extracted
Contour found of the above image
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread("/Users/2020shatgiskessell/Desktop/graph_extracting/Test_Graphs/Graph2.jpg")
h,w = img.shape[:2]
mask = np.zeros((h,w), np.uint8)
mask2 = mask = np.zeros((h,w), np.uint8)
def find_contours(image):
# Transform to gray colorspace and threshold the image
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# erod then dialate image (for denoising)
kernel = np.ones((2,2),np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
#Find contours in order of hiarchy
#CHAIN_APPROX_NONE gives all the points on the contour
_, contours, hierarchy = cv2.findContours(opening,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
return contours
#---------------------------------------------------------------
#CLEAN UP IMAGE AND JUST EXTRACT LINE
#get the biggest contour
cnt = max(find_contours(img), 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 of image
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.imwrite("extractedline.png", res)
#-------------------------------------------------------
#GET CONTOUR OF EXTRACTED LINE - NOT WORKING
i = 0
#Display contours
for contour in find_contours(res):
#approximate the contour shape
cv2.drawContours(mask2, [contour], 0, 255, -1)
res2 = cv2.bitwise_and(res,res,mask=mask2)
i = i+1
print (i)
cv2.imshow('after', mask2)

Categories