rectangle, Contour detection with python3, opencv3 - python-3.x

I want to detect paper sheet from image. i applied medianBlur, Canny , dilate, threshold, etc. algorithms to find.i am able to find sheet but don't know how to crop rectangle and apply transformation
answer sheet
This my code
import numpy as np
import cv2
image = cv2.imread('im_1.jpg')
image = cv2.resize(image, (800, 600))
draw = np.zeros_like(image)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
thresh = cv2.erode(thresh, kernel, iterations=4)
thresh = cv2.dilate(thresh, kernel, iterations=4)
im, cnts, hier = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
max_area = -1
max_c = 0
for i in range(len(cnts)):
contour = cnts[i]
area = cv2.contourArea(contour)
if (area > max_area):
max_area = area
max_c = i
contour = cnts[max_c]
rect = cv2.minAreaRect(contour)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(image, [box],-1, (0, 255, 0), 2)
cv2.imshow('Sheet', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
result of code:
result

There are some minor flaws in your approach. The following code will help. I have mentioned the changes made as well.
Code:
import numpy as np
import cv2
image = cv2.imread('C:/Users/Jackson/Desktop/score.jpg')
#--- Resized the image to half its dimension maintaining the aspect ratio ---
image = cv2.resize(image, (0, 0), fx = 0.5, fy = 0.5)
draw = np.zeros_like(image)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#--- I found the inverse binary image, because contours are found for objects in white. Since the border of the page is in black you have to invert the binary image. This is where it went wrong.
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)
#--- I did not perform any morphological operation ---
im, cnts, hier = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
max_area = -1
max_c = 0
for i in range(len(cnts)):
contour = cnts[i]
area = cv2.contourArea(contour)
if (area > max_area):
max_area = area
max_c = i
contour = cnts[max_c]
rect = cv2.minAreaRect(contour)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(image, [box],-1, (0, 255, 0), 2)
cv2.imshow('Sheet', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:

Related

search for multiple objects from an image

I need to find several objects (let's say up to 10 pieces) in the picture. Objects must be of a certain color.
Using this code and searching for contours, it turns out to find only the midpoint between several objects, but you need to find the coordinates of each object separately.
Maybe somehow you can make a limit on the size of one object? (i.e. the object must not be less than 100x100 pixels, and not more than 300x300 pixels)
import cv2
import matplotlib.pyplot as plt
import pyautogui as pg
output = '1.jpg'
flags = [i for i in dir(cv2) if i.startswith('COLOR_')]
nemo = cv2.imread(output, cv2.IMREAD_COLOR) #output, cv2.IMREAD_COLOR)
nemo = cv2.cvtColor(nemo, cv2.COLOR_BGR2RGB)
hsv_nemo = cv2.cvtColor(nemo, cv2.COLOR_RGB2HSV)
light_orange = (92,128,224)
dark_orange = (109,255,255)
#
mask = cv2.inRange(hsv_nemo, light_orange, dark_orange)
result = cv2.bitwise_and(nemo, nemo, mask=mask)
#
moments = cv2.moments(mask, 1)
x_moment = moments['m01']
y_moment = moments['m10']
area = moments['m00']
y = int(x_moment / area)
x = int(y_moment / area)
pg.moveTo(x,y)
plt.imshow(result)
plt.imsave('result.jpg',result)
plt.show()
#print(x,y)
gray = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(result, contours, -1, (0, 0, 255), 3)
cv2.imshow('img', result)
cv2.waitKey(0)
#print (len(contours[0]))
#print (len(contours[1]))
enter image description here

Masking colors using opencv

I tried to mask image by its color using opencv.
import cv2
import numpy as np
import matplotlib.pyplot as plt
After importing libraries, I load the image
img = cv2.imread('gmaps.jpg')
image = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
plt.imshow(image);
Turn the color into hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
plt.imshow(hsv);
Masking process
low_orange = np.array([44, 6, 100])
high_orange = np.array([44, 24, 99])
masking = cv2.inRange(hsv,low_orange, high_orange)
plt.imshow(masking);
The result isn't what I expected.
Image :
Result :
EDIT: I want to mask the building only. Instead I got the result of masking all of the frame.
Using my answer from here I manage to extract the right values for you
Code:
frame = cv2.imread("Xv6gx.png")
blurred_frame = cv2.GaussianBlur(frame, (5, 5), 0)
hsv = cv2.cvtColor(blurred_frame, cv2.COLOR_BGR2HSV)
lower = np.array([4, 0, 7])
upper = np.array([87, 240, 255])
mask = cv2.inRange(hsv, lower, upper)
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
for contour in contours:
area = cv2.contourArea(contour)
if area > 5000:
# -- Draw Option 1 --
cv2.drawContours(frame, contour, -1, (0, 255, 0), 3)
# -- Draw Option 2--
# rect = cv2.boundingRect(contour)
# x, y, w, h = rect
# cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow("Mask", mask)
cv2.imshow("Frame", frame)
cv2.waitKey(0)
Final Results:
I wouldn't expect the low Value (100) to exceed the high Value (99).
Also, OpenCV uses a range of 0..180 for Hue rather than 0..360, so you likely need to divide your 44 by 2.

Python Extract number from simple Image

I have the following image
lower = np.array([175, 125, 45], dtype="uint8")
upper = np.array([255, 255, 255], dtype="uint8")
mask = cv2.inRange(image, lower, upper)
img = cv2.bitwise_and(image, image, mask=mask)
plt.figure()
plt.imshow(img)
plt.axis('off')
plt.show()
now if I try to transform into grayscale like this:
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
I get that:
And I would like to extract the number on it.
The suggestion:
gray = 255 - gray
emp = np.full_like(gray, 255)
emp -= gray
emp[emp==0] = 255
emp[emp<100] = 0
gauss = cv2.GaussianBlur(emp, (3,3), 1)
gauss[gauss<220] = 0
plt.imshow(gauss)
gives the image:
Then using pytesseract on any of the images:
data = pytesseract.image_to_string(img, config='outputbase digits')
gives:
'\x0c'
Another suggested solution is:
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
thr = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV)[1]
txt = pytesseract.image_to_string(thr)
plt.imshow(thr)
And this gives
'\x0c'
Not very satisfying... Anyone has a better solution please?
Thanks!
I have a two step solution
Apply thresholding
Set psm mode to 7.
When you apply thresholding to the image:
Thresholding is a simplest method of displaying the features of the image.
Now from the output image, when we read:
txt = image_to_string(thr, config="--psm 7")
print(txt)
Result will be:
| 1,625 |
Now why do we set page-segmentation-mode (psm) mode to the 7?
Well, treating image as a single text line will give the accurate result.
But we have to modify the result. Since the current result is | 1,625 |
We should remove the |
print("".join([t for t in txt if t != '|']))
Result:
1,625
Code:
import cv2
from pytesseract import image_to_string
img = cv2.imread("LZ3vi.png")
gry = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thr = cv2.threshold(gry, 0, 255,
cv2.THRESH_BINARY_INV)[1]
txt = image_to_string(thr, config="--psm 7")
print("".join([t for t in txt if t != '|']).strip())
Update
how do you get this clean black and white image from my original image?
Using 3-steps
Reading the image using opencv's imread function
img = cv2.imread("LZ3vi.png")
Now we read the image in BGR fashion. (Not RGB)
Convert the image to the graysclae
gry = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
Result will be:
Apply threshold
thr = cv2.threshold(gry, 0, 255, cv2.THRESH_BINARY_INV)[1]
Result will be:
Now if you are wondering about thresholding. Read the simple-threhsolding
All my filters, grayscale... get weird colored images
The reason is, when you are displaying the image using pyplot, you need to set color-map (cmap) to gray
plt.imshow(img, cmap='gray')
You can read the other types here
Two issues blocked the pytessract from detecting your number:
The white rectangle around the number(Inverting and filling is the solution).
The Noise in the numbers shape(Gaussian Smoothing dealt with that)
The solution that AlexAlex has proposed will work perfectly if it was followed by a Gaussian filter:
output: 1,625
import numpy as np
import pytesseract
import cv2
BGR = cv2.imread('11.png')
RGB = cv2.cvtColor(BGR, cv2.COLOR_BGR2RGB)
lower = np.array([175, 125, 45], dtype="uint8")
upper = np.array([255, 255, 255], dtype="uint8")
mask = cv2.inRange(RGB, lower, upper)
img = cv2.bitwise_and(RGB, RGB, mask=mask)
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
gray = 255 - gray
emp = np.full_like(gray, 255)
emp -= gray
emp[emp==0] = 255
emp[emp<100] = 0
gauss = cv2.GaussianBlur(emp, (3,3), 1)
gauss[gauss<220] = 0
text = pytesseract.image_to_string(gauss, config='outputbase digits')
print(text)

Crop the rectangular paper from the image

from the discussion : Crop exactly document paper from image
I'm trying to get the white paper from the image and I'm using the following code which not cropping exactly rectangular.
def crop_image(image):
image = cv2.imread(image)
# convert to grayscale image
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# threshold
thresh = cv2.threshold(gray, 190, 255, cv2.THRESH_BINARY)[1]
# apply morphology
kernel = np.ones((7, 7), np.uint8)
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
kernel = np.ones((9, 9), np.uint8)
morph = cv2.morphologyEx(morph, cv2.MORPH_ERODE, kernel)
# Get Largest contour
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
contours = contours[0] if len(contours) == 2 else contours[1]
area_thresh = 0
for cnt in contours:
area = cv2.contourArea(cnt)
if area > area_thresh:
area_thresh = area
big_contour = cnt
# get bounding box
x, y, w, h = cv2.boundingRect(big_contour)
# draw filled contour on black background
mask = np.zeros_like(gray)
mask = cv2.merge([mask, mask, mask])
cv2.drawContours(mask, [big_contour], -1, (255, 255, 255), cv2.FILLED)
# apply mask to input
result = image.copy()
result = cv2.bitwise_and(result, mask)
# crop result
img_result = result[y:y+h, x:x+w]
filename = generate_filename()
cv2.imwrite(filename, img_result)
logger.info('Successfully saved cropped file : %s' % filename)
return img_result, filename
I'm able to get the desired result but not the rectangular image.
Here I'm attaching and here is what I'm getting after cropping image .
I want a rectangular image of the paper.
Please help me with this.
Thanks in advance
The first problem I can see is that the threshold value is not low enough so the bottom part of the paper is not correctly capture (it's too dark to be captured by the threshold)
The second problem as far I can understand is being able to fit the square to the image. What you need to do is wrapping perspective.
To do that you can find more information in this amazing post of PyImageSearch

Iam using python but i can not handel detect QR code using openCV

I am not able to detect the QR code in registration certificate image
# import the necessary packages
import cv2
import imutils
import numpy as np
from pyzbar import pyzbar
image = cv2.imread("myimages/adhar1.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# compute the Scharr gradient magnitude representation of the images
# in both the x and y direction using OpenCV 2.4
ddepth = cv2.cv.CV_32F if imutils.is_cv2() else cv2.CV_32F
gradX = cv2.Sobel(gray, ddepth=ddepth, dx=1, dy=0, ksize=-1)
gradY = cv2.Sobel(gray, ddepth=ddepth, dx=0, dy=1, ksize=-1)
# subtract the y-gradient from the x-gradient
gradient = cv2.subtract(gradX, gradY)
gradient = cv2.convertScaleAbs(gradient)
cv2.imshow("gradient", gradient)
cv2.waitKey()
cv2.destroyAllWindows()
# blur and threshold the image
blurred = cv2.blur(gradient, (3, 3))
cv2.imshow("blurred", blurred)
cv2.waitKey()
cv2.destroyAllWindows()
(_, thresh) = cv2.threshold(blurred, 225, 255, cv2.THRESH_BINARY)
cv2.imshow("thresh", thresh)
cv2.waitKey()
cv2.destroyAllWindows()
# construct a closing kernel and apply it to the thresholded image
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# perform a series of erosions and dilations
closed = cv2.erode(closed, None, iterations=4)
closed = cv2.dilate(closed, None, iterations=4)
cv2.imshow("Image22.jpg", closed)
cv2.waitKey()
#find the contours in the thresholded image, then sort the contours
# by their area, keeping only the largest one
cnts = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
c = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
# compute the rotated bounding box of the largest contour
rect = cv2.minAreaRect(c)
box = cv2.cv.BoxPoints(rect) if imutils.is_cv2() else cv2.boxPoints(rect)
box = np.int0(box)
# print(box)
# draw a bounding box arounded the detected barcode and display the
order_points = image[box[1][1]:box[3][1],box[1][0]:box[3][0]]
cv2.imwrite("test.jpg", order_points)
barcode = pyzbar.decode(order_points)
print(barcode)
cv2.drawContours(image, [box], -1, (0, 0, 255), 3)
# cv2.imshow("Image", image)
cv2.imshow("Image1.jpg", image)
barcoad = pyzbar.decode(image)
print(barcoad)
cv2.waitKey(0)
Hear am using open-CV for Barbados image detection but am not able to find registration certificate(rc). Please give me a better solution for thin problem.

Resources