Extract MSER detected areas (Python, OpenCV) - python

I can't extract the detected regions by MSER in this image:
What I want to do is to save the green bounded areas.
My actual code is this:
import cv2
import numpy as np
mser = cv2.MSER_create()
img = cv2.imread('C:\\Users\\Link\\img.tif')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
vis = img.copy()
regions, _ = mser.detectRegions(gray)
hulls = [cv2.convexHull(p.reshape(-1, 1, 2)) for p in regions]
cv2.polylines(vis, hulls, 1, (0, 255, 0))
mask = np.zeros((img.shape[0], img.shape[1], 1), dtype=np.uint8)
mask = cv2.dilate(mask, np.ones((150, 150), np.uint8))
for contour in hulls:
cv2.drawContours(mask, [contour], -1, (255, 255, 255), -1)
text_only = cv2.bitwise_and(img, img, mask=mask)
cv2.imshow('img', vis)
cv2.waitKey(0)
cv2.imshow('mask', mask)
cv2.waitKey(0)
cv2.imshow('text', text_only)
cv2.waitKey(0)
Expected result should be a ROI like image.
Source image:

detectRegions also returns bounding boxes:
regions, boundingBoxes = mser.detectRegions(gray)
for box in boundingBoxes:
x, y, w, h = box;
cv2.rectangle(vis, (x, y), (x+w, y+h), (0, 255, 0), 1)
This draws green rectangles, or save them as mentioned in GPhilo's answer.

Just get the bounding box for each contour, use that as a ROI to extract the area and save it out:
for i, contour in enumerate(hulls):
x,y,w,h = cv2.boundingRect(contour)
cv2.imwrite('{}.png'.format(i), img[y:y+h,x:x+w])

Hey found a cleaner way to get the bounding boxes
regions, _ = mser.detectRegions(roi_gray)
bounding_boxes = [cv2.boundingRect(p.reshape(-1, 1, 2)) for p in regions]

Related

problem with shared boundaries of contours in opencv

I am trying to get the maximum area object in an image.
I applied a Blur Kernel 5x5 then I applied the Canny algo to get the edges. Then I used the findContours method and the max contourArea but it returns the wrong object.
Base Image:
Canny Image:
Image with all contours found:
Max area object:
As you can see it has to return the left box but it returns the right one.
I think the problem is that left and right boxes share a common edge but it seems that belongs only to the left one.
This is the code snippet:
img_rgb = cv.imread(img_path)
gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY)
# blur with a kernel size of 5x5
blur = cv.GaussianBlur(gray, (5, 5), 0)
canny = cv.Canny(blur, 50, 50)
#saving canny image
cv.imwrite("canny.png", canny)
_, thresh = cv.threshold(canny, 127, 255, 0)
contours, _ = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
for c in contours:
cv.drawContours(img_rgb, [c], 0, (randrange(255), randrange(255), randrange(255)), 3)
#saving image with contours
cv.imwrite("contours.png", img_rgb)
max_area_contour = max(contours, key=cv.contourArea)
x, y, w, h = cv.boundingRect(max_area_contour)
cv.rectangle(img_rgb, (x, y), (x + w, y + h), (0, 255, 0), 3)
#saving the image with the biggest contour
cv.imwrite("max_contour.png", img_rgb)
I made key function for max as a square of bounding rect and then it works fine:
img_rgb = cv.imread(img_path)
img_rgb_init = img_rgb.copy()
gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY)
# blur with a kernel size of 5x5
blur = cv.GaussianBlur(gray, (5, 5), 0)
canny = cv.Canny(blur, 50, 50)
#saving canny image
# cv.imwrite("canny.png", canny)
_, thresh = cv.threshold(canny, 127, 255, 0)
contours, _ = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
for c in contours:
cv.drawContours(img_rgb, [c], 0, (random.randrange(255), random.randrange(255), random.randrange(255)), 3)
#saving image with contours
# cv.imwrite("contours.png", img_rgb)
def bounding_rect_size(in_cnt):
x, y, w, h =cv.boundingRect(in_cnt)
return w*h
x, y, w, h = cv.boundingRect(max(contours,key=bounding_rect_size))
cv.rectangle(img_rgb_init, (x, y), (x + w, y + h), (0, 255, 0), 3)
cv.imshow("",img_rgb_init)
cv.waitKey()
The reason it was working incorrect that the contour of bigest rectangle was looking like train railways. In one direction from one side of rectangle line and in other direction from other side (the line somewhere is broken). That is why the area of this contour was small.

How to detect White region in an image with Opencv & Python?

I'm trying to extract the coordinates of a big white region in an image as follows:
Here's the original image:
Using a small square kernel, I applied a closing operation to fill small holes and help identify larger structures in the image as follows:
import cv2
import numpy as np
import imutils
original = cv2.imread("Plates\\24.png")
original = cv2.resize(original, None, fx=3, fy=3, interpolation=cv2.INTER_CUBIC)
gray = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY)
# next, find regions in the image that are light
squareKern = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
light = cv2.morphologyEx(gray, cv2.MORPH_CLOSE, squareKern)
light = cv2.threshold(light, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
the resulting image is as follows:
Here's another example:
What I wish to be able to do is to detect the large white region in the plate as follows:
Keeping in mind that contours will not work well with many examples
With the one image you provided:
I came up with 2 approaches as to how this problem can be solved:
Approach 1
Contour Area Comparison
As you can see there are 3 large contours in the image; the top rectangle and the two rectangles below it, of which you want to detect as a whole.
So I used a threshold on your image, detected the contours of the thresholded image, and indexed the second largest contour and the third largest contour (the largest is the top rectangle which you want to ignore).
Here is the thresholded image:
I stacked the two contours together and detected the bounding box of the two contours:
import cv2
import numpy as np
img = cv2.imread("image.png")
def process(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(img_gray, 128, 255, cv2.THRESH_BINARY)
img_blur = cv2.GaussianBlur(thresh, (5, 5), 2)
img_canny = cv2.Canny(img_blur, 0, 0)
return img_canny
def get_contours(img):
contours, _ = cv2.findContours(process(img), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
r1, r2 = sorted(contours, key=cv2.contourArea)[-3:-1]
x, y, w, h = cv2.boundingRect(np.r_[r1, r2])
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
get_contours(img)
cv2.imshow("img_processed", img)
cv2.waitKey(0)
Output:
Approach 2
Threshold Masking
As the 2 bottom rectangles are whiter than the top rectangle of the plate, I used a threshold to mask out the top of the plate:
I used the canny edge detector on the mask shown above.
import cv2
def process(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(img_gray, 163, 255, cv2.THRESH_BINARY)
img_canny = cv2.Canny(thresh, 0, 0)
img_dilate = cv2.dilate(img_canny, None, iterations=7)
return cv2.erode(img_dilate, None, iterations=7)
def get_contours(img):
contours, _ = cv2.findContours(process(img), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
x, y, w, h = cv2.boundingRect(max(contours, key=cv2.contourArea))
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
img = cv2.imread("egypt.png")
get_contours(img)
cv2.imshow("img_processed", img)
cv2.waitKey(0)
Output:
Of course, this method may not work properly if the top of the plate isn't brighter than the bottom.

How to find whether there is single image or grid of images present in single frame using python

Here is the image:
How to find that there are total 9 images present in this frame?
import matplotlib.pyplot as plt
import numpy as np
img = cv2.imread('viber_image_2020-05-13_16-47-36.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
thresh_inv = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[2]
Blur the image
blur = cv2.GaussianBlur(thresh_inv,(1,1),0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[2]
Find contours
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
mask = np.ones(img.shape[:2], dtype="uint8") * 255
for c in contours:
# get the bounding rect
x, y, w, h = cv2.boundingRect(c)
if w*h>1000:
cv2.rectangle(mask, (x, y), (x+w, y+h), (0, 0, 255), -1)
res_final = cv2.bitwise_and(img, img, mask=cv2.bitwise_not(mask))
cv2.imshow("boxes", mask)
cv2.imshow("final image", res_final)
cv2.waitKey(0)
cv2.destroyAllWindows()
You can use cv2.Laplacian(gray_image) and np.mean() this (axis=0, axis= 1) for find grid edges.

Difficulty extracting characters using MSER in images which have borders around the text

I am trying to develop a OCR system. I am trying to use MSER in order to extract character from an image and then passing the characters into a CNN to recognize those characters. Here is my code for character extraction:
import cv2
import numpy as np
# create MSER object
mser = cv2.MSER_create()
# read the image
img = cv2.imread('textArea01.png')
# convert to gray scale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# store copy of the image
vis = img.copy()
# detect regions in the image
regions,_ = mser.detectRegions(gray)
# find convex hulls of the regions and draw them onto the original image
hulls = [cv2.convexHull(p.reshape(-1, 1, 2)) for p in regions]
cv2.polylines(vis, hulls, 1, (0, 255, 0))
# create mask for the detected region
mask = np.zeros((img.shape[0], img.shape[1], 1), dtype=np.uint8)
mask = cv2.dilate(mask, np.ones((150, 150), 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('img', vis)
cv2.waitKey(0)
cv2.imshow('mask', mask)
cv2.waitKey(0)
cv2.imshow('text', text_only)
cv2.waitKey(0)
This is working fine for most images, but for some images like this:
The outer border is also detected as a region and the contour is drawn in the mask such that all area inside the border is detected as text region. So, the contours inside have no effect. How do I prevent this so that only the text is detected?
Hulls detected:
and the mask as a result:
My result using this code:
import cv2
import numpy as np
img = cv2.imread("img.png")
# grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow('gray', gray)
# binary
# ret, thresh = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY_INV)
thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 35, 180)
cv2.imshow('threshold', thresh)
# dilation
kernel = np.ones((1, 1), np.uint8)
img_dilation = cv2.dilate(thresh, kernel, iterations=1)
cv2.imshow('dilated', img_dilation)
# find contours
# cv2.findCountours() function changed from OpenCV3 to OpenCV4: now it have only two parameters instead of 3
cv2MajorVersion = cv2.__version__.split(".")[0]
# check for contours on thresh
if int(cv2MajorVersion) >= 4:
ctrs, hier = cv2.findContours(img_dilation.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
else:
im2, ctrs, hier = cv2.findContours(img_dilation.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# sort contours
sorted_ctrs = sorted(ctrs, key=lambda ctr: cv2.boundingRect(ctr)[0])
for i, ctr in enumerate(sorted_ctrs):
# Get bounding box
x, y, w, h = cv2.boundingRect(ctr)
# Getting ROI
roi = img[y:y + h, x:x + w]
# show ROI
# cv2.imshow('segment no:'+str(i),roi)
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 1)
# if you want to save the letters without green bounding box, comment the line above
if w > 5:
cv2.imwrite('C:\\Users\\PC\\Desktop\\output\\{}.png'.format(i), roi)
cv2.imshow('marked areas', img)
cv2.waitKey(0)
You can have a threshold on the contour area so that it ignores all shapes that cover more than a certain area in the image.
for contour in hulls:
if cv.contourArea(contour) < ThresholdArea:
continue
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)

Transparent background of irregular ROI shape (Python, OpenCV)

I am trying to extract the number from this image using MSER contours.
Using this code..
import cv2
import numpy as np
mser = cv2.MSER_create()
img = cv2.imread('C:\\Users\\Link\\Desktop\\7.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
vis = img.copy()
regions, _ = mser.detectRegions(gray)
hulls = [cv2.convexHull(p.reshape(-1, 1, 2)) for p in regions]
poly = cv2.polylines(vis, hulls, 1, (0, 255, 0))
mask = np.zeros((img.shape[0], img.shape[1], 1), dtype=np.uint8)
mask = cv2.dilate(mask, np.ones((150, 150), np.uint8))
for i,contour in enumerate(hulls):
cv2.drawContours(mask, [contour], -1, (255, 255, 255), -1)
text_only = cv2.bitwise_and(img, img, mask=mask)
cv2.imwrite('poly{}.png'.format(i), text_only)
cv2.imshow('img', vis)
cv2.waitKey(0)
cv2.imshow('mask', mask)
cv2.waitKey(0)
cv2.imshow('text', text_only)
cv2.waitKey(0)
...I obtain this as image (apart from the whole image itself as result of MSER detection):
How can I made the background transparent ? All that I found about this procedure, which use Opencv and Python, is this other question: NumPy/OpenCV 2: how do I crop non-rectangular region?
Applying the code suggested there produce the next output, which is not what I would like to have:
The goal is to have something like this:
If it's not possible at that level, atleast how can I turn the black area into transparent ?
Thanks

Categories