Grey scale image to contour with isotherm lines - python

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:

Related

An output image file with red contours of all objects

I have the following code:
import cv2 as cv
import numpy as np
image = cv.imread("input1.jpg")
img_gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
img_denoised = cv.GaussianBlur(img_gray,(5,5),2)
ret, thresh = cv.threshold(img_denoised, 216, 255, cv.THRESH_BINARY)
kernel = np.ones((1,1),np.uint8)
opening = cv.dilate(thresh, kernel)
opening = cv.erode(opening, kernel)
# detect the contours on the binary image using cv.CHAIN_APPROX_NONE
contours, hierarchy = cv.findContours(image=opening, mode=cv.RETR_TREE, method=cv.CHAIN_APPROX_NONE)
for i in contours:
x, y, w, h = cv.boundingRect(i)
cv.drawContours(image, [i], -1, (0, 0, 255), 2)
cv.imshow("A.jpg", image)
cv.waitKey(0)
cv.destroyAllWindows()
Output:
enter image description here
It only shows the stars with a red contours but I want all the text to have a red contours, including the background. Here is the original file:
enter image description here
Many thanks in advance!
I messed with this a bit and the best outcome I could get was the following, I think with some tweaking you could ignore the shading, as I'm converting it to grayscale it seems to be dropping the correct contour on the shapes, but the text is working as expected;
import cv2
import numpy as np
src = cv2.imread('c:\\input1.jpg')
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
# blur
blur = cv2.GaussianBlur(gray, (3, 3), 0)
# canny edge
canny = cv2.Canny(blur, 100, 200)
# dilate
kernel = np.ones((5, 5))
dilate = cv2.dilate(canny, kernel, iterations=1)
# find contours
contours, hierarchy = cv2.findContours(
dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# draw contours
cv2.drawContours(src, contours, -1, (0, 255, 0), 3)
cv2.imshow("a.jpg", src)
cv2.waitKey()

Hand contour not precise

I wrote an OpenCV program to extract the hand out of the image precisely. But is not able to get it out correctly. Below is the code and the output and the sample image which I used to test it.
import numpy as np
import cv2
# Reading image
font = cv2.FONT_HERSHEY_COMPLEX
img2 = cv2.imread('1.bmp', cv2.IMREAD_COLOR)
# Reading same image in another
# variable and converting to gray scale.
img = cv2.imread('1.bmp', cv2.IMREAD_GRAYSCALE)
# Converting image to a binary image
# ( black and white only image).
_, threshold = cv2.threshold(img, 110, 255, cv2.THRESH_BINARY)
# Detecting contours in image.
contours, _= cv2.findContours(threshold, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
contours1 = max(contours, key=cv2.contourArea)
# Going through every contours found in the image.
approx = cv2.approxPolyDP(contours1, 0.009 * cv2.arcLength(contours1, True), True)
# draws boundary of contours.
cv2.drawContours(img2, [approx], 0, (0, 0, 255), 5)
cv2.imshow('image2', img2)
# Exiting the window if 'q' is pressed on the keyboard.
if cv2.waitKey(0) & 0xFF == ord('q'):
cv2.destroyAllWindows()
Input image -
One of the reasons your contour is not precise is the obvious; the line where you approximated the contour. But you have also mentioned (in a comment) that lowering the approximation didn't solve the problem.
This is because you didn't blur the thresholded image, which resulted in the jagged edges. Here is an example where the thresholded image is blurred before the contour detection:
The code:
import cv2
import numpy as np
def process(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
return cv2.threshold(img_gray, 111, 255, cv2.THRESH_BINARY)[1]
def draw_contours(img):
contours, _ = cv2.findContours(process(img), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)
cv2.drawContours(img, [cnt], -1, (0, 0, 255), 2)
img = cv2.imread("image.png")
draw_contours(img)
cv2.imshow("result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Input image:
Output image:
Still, the contour isn't very precise. This is where the Canny edge detector comes into play:
import cv2
import numpy as np
def process(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(img_gray, 111, 255, cv2.THRESH_BINARY)
img_blur = cv2.GaussianBlur(thresh, (5, 5), 4)
img_canny = cv2.Canny(img_blur, 0, 0)
img_dilate = cv2.dilate(img_canny, None, iterations=1)
return cv2.erode(img_dilate, None, iterations=0)
def draw_contours(img):
contours, _ = cv2.findContours(process(img), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)
cv2.drawContours(img, [cnt], -1, (0, 0, 255), 2)
img = cv2.imread("image.png")
draw_contours(img)
cv2.imshow("result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Get Edges/boundary for overlapping image

I have a mask for a dental x-ray here where all teeth are overlapping with each other. I want to count the number of teeth present in the image for that I want to separate overlapping tooths so I can use contour-based approach to count the number of tooths, I tried following approach but it is giving result like this
. how can I extract boundary for each tooth?
from skimage.feature import peak_local_max
from skimage.morphology import watershed
import matplotlib.pyplot as plt
from scipy import ndimage
import numpy as np
import cv2
def getImageEdge(input_image):
img_gray = input_image
image_black = np.zeros(shape=input_image.shape, dtype="uint8")
thresh = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
thresh_copy = thresh.copy()
D = ndimage.distance_transform_edt(thresh_copy)
localMax = peak_local_max(D, indices = False, min_distance = 12, labels = thresh)
markers = ndimage.label(localMax, structure = np.ones((3, 3)))[0]
labels = watershed(-D, markers, mask = thresh_copy)
for label in np.unique(labels):
if label == 0:
continue
mask = np.zeros(img_gray.shape, dtype = "uint8")
mask[labels == label] = 255
contours, hierarchy = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image_black, contours, -1, (255, 255, 255), 1)
return image_black
inputImage = cv2.imread("/content/dentalMask.bmp")
inputImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)
outputImage = getImageEdge(inputImage)
plt.imshow(inputImage)
plt.show()
plt.imshow(outputImage)
plt.show()
EDITED:
Based on the answer from fmw42 I have added one more image where it is showing more overlapping and failed in simple thresholding and contour-based approach.
input
output
Given your example, the following works for me in Python/OpenCV by simply thresholding and getting the contours.
Input:
import cv2
import numpy as np
# read image
img = cv2.imread("teeth.png")
# convert img to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 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]
# Get contours
cntrs = cv2.findContours(thresh, 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("")
# write results to disk
cv2.imwrite("teeth_thresh.png", thresh)
cv2.imwrite("tide_contours.png", result)
# display it
cv2.imshow("thresh", thresh)
cv2.imshow("result", result)
cv2.waitKey(0)
Contours:
Resulting Count:
count = 32
Without knowledge of the exact layout of teeth in a mouth, this task is impossible. No image processing technique can help.
Because in case of touching teeth, you can't tell two touching teeth from a two-rooted tooth.

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:

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()

Categories