How to save OpenCV image with contour - python

I want to save image with contour
Here is my code:
img = cv2.imread('123.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
image, contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
# some code in here
cv2.imwrite('234.jpg', cnt)
Thanks a lot.

What you want to do is to create a mask that you draw the contours on to, then use that to snip out the rest of the picture, or vice-versa. For instance, based on this tutorial:
(contours, _) = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
mask = np.ones(img.shape[:2], dtype="uint8") * 255
# Draw the contours on the mask
cv2.drawContours(mask, contours, -1, 0, -1)
# remove the contours from the image and show the resulting images
img = cv2.bitwise_and(img, img, mask=mask)
cv2.imshow("Mask", mask)
cv2.imshow("After", img)
cv2.waitKey(0)

The easiest way to save the contour as image is taking out its ROI(region of image) and saving it using imwrite() as follows -
First use cv2.boundingRect to get the bounding rectangle for a set of points (i.e. contours):
x, y, width, height = cv2.boundingRect(contours[i])
You can then use NumPy indexing to get your ROI from the image:
roi = img[y:y+height, x:x+width]
And save the ROI to a new file:
cv2.imwrite("roi.png", roi)

I was trying many times and finally, I could make it:
image= cv2.imread('muroprueba.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
cnts, herarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image,cnts,-1,(0,255,0),1)
cv2.imshow('image1',image)
cv2.waitKey(0)
cv2.imwrite('F:\caso1.jpg',image) #Save the image
cv2.destroyAllWindows()

Related

Grey scale image to contour with isotherm lines

I have gray scale image and want to convert to intensity contour with isotherm lines, in my code I am getting only one contour and how to apply the isotherm lines?
Goal:
import numpy as np
import cv2 as cv
img = cv2.imread(path)
imgray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(imgray, 127, 255, 0)
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
cv.drawContours(img, contours, -1, (0,255,0), 3)
plt.imshow(img)
You're on the right track, all you have to do is just take that 127 that you hard-coded into the code, and iterate over a couple of different values. So take what you have and just add a few things (including a plug for the viridis colormap):
import numpy as np
import cv2
# I don't have your image, so I will just create a similar one.
H, W = 480, 640
img = np.zeros([H, W, 3], dtype=np.uint8)
cv2.circle(img, (W//2, H//2), 200, (255,255,255), -1)
img = cv2.GaussianBlur(img, (551, 551), 0)
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# The viridis colormap is better than the jet one you have used.
img_viridis = cv2.applyColorMap(imgray, cv2.COLORMAP_VIRIDIS)
# This for-loop allows you to draw isotherm lines at any value you want.
THRESHES = [30, 90, 170]
for val in THRESHES:
ret, thresh = cv2.threshold(imgray, val, 255, 0)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img_viridis, contours, -1, (0, 0, 255), 2)
cv2.imshow('img', img_viridis)
k = cv2.waitKey(0)
output:
Here is another approach in Python/OpenCV by quantizing the gray image and then getting the contours.
Read the input
Convert it to gray
Quantize it
Get Canny edge
Apply morphology close to ensure they are closed
Get the contours
Filter the contours by perimeter to remove small extraneous ones
Draw the contours on the input
Save the results
Input:
import numpy as np
import cv2
# read input
img = cv2.imread('bright_blob.png')
# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# do color quantization
gray = 64*((gray/64).astype(np.uint8))
# get canny edges
edges = cv2.Canny(gray, 10, 250)
# apply morphology closed to ensure they are closed
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
edges = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)
# get contours
contours = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
contours = contours[0] if len(contours) == 2 else contours[1]
# filter contours to keep only large ones
result = img.copy()
for c in contours:
perimeter = cv2.arcLength(c, True)
if perimeter > 200:
cv2.drawContours(result, c, -1, (0,0,255), 1)
# save results
cv2.imwrite("bright_blob_gray.jpg", gray)
cv2.imwrite("bright_blob_edges.jpg", edges)
cv2.imwrite("bright_blob_isotherms.jpg", result)
# show images
cv2.imshow("gray", gray)
cv2.imshow("edges", edges)
cv2.imshow("result", result)
cv2.waitKey(0)
Quantized gray image:
Edge image:
Result:

Crop exactly document paper from image

I am using the following code to crop image currently
def crop_image(image):
image = cv2.imread(image)
original_img = image.copy()
hsv_img = convert_hsv(image)
lower_blue = np.array([0, 0, 120])
upper_blue = np.array([180, 38, 255])
masked_image = mask_img(hsv_img, lower_blue, upper_blue)
result = cv2.bitwise_and(image, image, mask=masked_image)
contours = cv2.findContours(masked_image.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
contours = imutils.grab_contours(contours)
cv2.drawContours(masked_image, contours, -1, (0, 255, 0), 3)
max_area_contour = max(contours, key=cv2.contourArea)
x, y, w, h = cv2.boundingRect(max_area_contour)
cv2.rectangle(result, (x, y), (x+w, y+h), (0, 255, 0), 3)
cont_filename = generate_contours_filename()
cv2.imwrite(cont_filename, np.hstack([image, result]))
logger.info('Successfuly saved file : %s' % cont_filename)
img = image[y:y+h, x:x+w]
filename = generate_filename()
cv2.imwrite(filename, img)
logger.info('Successfully saved cropped file : %s' % filename)
return img, filename
Following are theimages before and after:
This is original image
This is resulting image
I need image that crops paper part only
Thanks in advance
Here is one way to do that in Python/Opencv.
Read the input
Convert to grayscale
Threshold
Apply morphology to clean it of small regions
Get contours and filter to keep the largest one
Get the bounding box
Draw the largest contour filled on a black background as a mask
Apply the mask to blacken out the background of the paper
Use the bounding box to crop the masked input
Save the results
Input:
import cv2
import numpy as np
# read image as grayscale
img = cv2.imread('paper.jpg')
# convert to grayscale
gray = cv2.cvtColor(img,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 c in contours:
area = cv2.contourArea(c)
if area > area_thresh:
area_thresh = area
big_contour = c
# 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
result1 = img.copy()
result1 = cv2.bitwise_and(result1, mask)
# crop result
result2 = result1[y:y+h, x:x+w]
# view result
cv2.imshow("threshold", thresh)
cv2.imshow("morph", morph)
cv2.imshow("mask", mask)
cv2.imshow("result1", result1)
cv2.imshow("result2", result2)
cv2.waitKey(0)
cv2.destroyAllWindows()
# save result
cv2.imwrite("paper_thresh.jpg", thresh)
cv2.imwrite("paper_morph.jpg", morph)
cv2.imwrite("paper_mask.jpg", mask)
cv2.imwrite("paper_result1.jpg", result1)
cv2.imwrite("paper_result2.jpg", result2)
Thresholded image:
Morphology cleaned image:
Mask image from largest contour:
Result of masking the input:
Result of cropping previous image:

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)

Removing contours from an image

I have contours which i want to delete from the image, What is the best way to do it ?
image = cv2.imread(path)
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
retr , thresh = cv2.threshold(gray_image, 190, 255, cv2.THRESH_BINARY_INV)
contours, hier = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
if cv2.contourArea(c) > 20:
x, y, w, h = cv2.boundingRect(c)
##### how to continue from here ?
Create an empty mask in the size of the image:
mask = np.zeros(image.shape[:2], dtype=image.dtype)
Next draw all the contours / boundingrect you want to keep on this mask:
cv2.drawContours(mask, [cnt], 0, (255), -1)
Alternatively you can instead draw the contours you don't want and inverse the mask (this may be more suitable in some situations):
mask= cv2.bitwise_not(mask)
Use the mask on the main image:
result = cv2.bitwise_and(image,image, mask= mask)
Edit: added code after comment.
I assumed this is about the image in your other question, so I applied the code to that image.
Result:
Code:
import numpy as np
import cv2
# load image
image = cv2.imread('image.png')
# create grayscale
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# perform threshold
retr , thresh = cv2.threshold(gray_image, 190, 255, cv2.THRESH_BINARY_INV)
# find contours
ret, contours, hier = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# create emtpy mask
mask = np.zeros(image.shape[:2], dtype=image.dtype)
# draw all contours larger than 20 on the mask
for c in contours:
if cv2.contourArea(c) > 20:
x, y, w, h = cv2.boundingRect(c)
cv2.drawContours(mask, [c], 0, (255), -1)
# apply the mask to the original image
result = cv2.bitwise_and(image,image, mask= mask)
#show image
cv2.imshow("Result", result)
cv2.imshow("Image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV ROI background padding

I am trying to focus on a Region of Interest on the face by using Numpy cropping. For large images, I have no trouble. But for smaller ones, I can find the face and find the matching bounding box, but when I try to crop it and show the image, I get a gray background to the right of the image. I wouldn't mind it, but when I try to apply contours to the image, the bottom and right edges are picked up as contour edges. How do I make sure that the gray padding doesn't affect my contour?
Here is my function:
def head_ratio(self):
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
faces = face_cascade.detectMultiScale(self.gray, 1.3, 5)
m_height, m_width, channels = self.image.shape
x, y, w, h = faces[0]
ROI = self.image[y:y+h,x:x+w]
kernel = np.ones((5,5), np.float32)/10
blurred = cv2.filter2D(self.gray[y:y+h, x:x+w], -1, kernel)
ret, thresh = cv2.threshold(blurred, 127, 255, cv2.THRESH_BINARY)
_,contours,_ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(ROI, contours, -1, (255, 255, 255), 3)
cv2.imshow("output", ROI)
cv2.waitKey(0)
cv2.destroyAllWindows()
return 1
Thank you for your help. I have been trying to look for an answer to this but was having trouble with what to search for.

Categories