how to remove skull in brain MRI by using watershed - python

I'm working on detecting the brain tumor by using python. I need some help for the segmentation process in my code. I want to remove the skull part in the brain MRI. I'm using the watershed algorithm to define the skull in the MRI images. however, the tumor region is also shaded by the code. how can I change the threshold detected by the code in order to keep the whole brain region untouched by the algorithm. the picture shown below is a sample of what i want to get. ` import numpy as np
import cv2
from matplotlib import pyplot as plt
from skimage.morphology import extrema
from skimage.morphology import watershed as skwater
def ShowImage(title,img,ctype):
plt.figure(figsize=(10, 10))
if ctype=='bgr':
b,g,r = cv2.split(img) # get b,g,r
rgb_img = cv2.merge([r,g,b]) # switch it to rgb
plt.imshow(rgb_img)
elif ctype=='hsv':
rgb = cv2.cvtColor(img,cv2.COLOR_HSV2RGB)
plt.imshow(rgb)
elif ctype=='gray':
plt.imshow(img,cmap='gray')
elif ctype=='rgb':
plt.imshow(img)
else:
raise Exception("Unknown colour type")
plt.axis('off')
plt.title(title)
plt.show()
img = cv2.imread('s1.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ShowImage('Brain MRI',gray,'gray')
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_OTSU)
ShowImage('Thresholding image',thresh,'gray')
ret, markers = cv2.connectedComponents(thresh)
#Get the area taken by each component. Ignore label 0 since this is the background.
marker_area = [np.sum(markers==m) for m in range(np.max(markers)) if m!=0]
#Get label of largest component by area
largest_component = np.argmax(marker_area)+1 #Add 1 since we dropped zero above
#Get pixels which correspond to the brain
brain_mask = markers==largest_component
brain_out = img.copy()
#In a copy of the original image, clear those pixels that don't correspond to the brain
brain_out[brain_mask==False] = (0,0,0)
img = cv2.imread('s1.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# noise removal
kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)
# sure background area
sure_bg = cv2.dilate(opening,kernel,iterations=3)
# Finding sure foreground area
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5)
ret, sure_fg = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)
# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg,sure_fg)
# Marker labelling
ret, markers = cv2.connectedComponents(sure_fg)
# Add one to all labels so that sure background is not 0, but 1
markers = markers+1
# Now, mark the region of unknown with zero
markers[unknown==255] = 0
markers = cv2.watershed(img,markers)
img[markers == -1] = [255,0,0]
im1 = cv2.cvtColor(img,cv2.COLOR_HSV2RGB)
ShowImage('Watershed segmented image',im1,'gray')
brain_mask = np.uint8(brain_mask)
kernel = np.ones((8,8),np.uint8)
closing = cv2.morphologyEx(brain_mask, cv2.MORPH_CLOSE, kernel)
ShowImage('Closing', closing, 'gray')
brain_out = img.copy()
#In a copy of the original image, clear those pixels that don't correspond to the brain
brain_out[closing==False] = (0,0,0)`
the picture shown below is a sample of what i want to get.this is what i get when running the code this is the original image

Related

How can I connect a gap in a white frame on black background with python opencv?

I developed a frame detection algorithm in python using opencv that detects a metallic frame in a rgb image. The output is a white frame on a black background. However, if the frame is partly covered, it contains gaps. How can I fill the gaps, preferably using opencv?
Example output of my algorithm:
Frame with gap
Desired output:
Frame without gap
Edit:
This is how I detect the frame currently.
I have an rgb image of a crop canopy and a frame on top of it:
Crop canopy image
My code detects bright parts in the image and removes noise such that only the frame should be left (which is the largest brigth object in the image). Below is a reproducible example when setting image path to the example image above:
# import packages
import numpy as np
import cv2
kernel_size = 10
brightness_thres_low = 175
brightness_thres_high = 255
erode_it_1 = 6
dilate_it_1 = 25
erode_it_2 = 20
# read image
img = cv2.imread(image_path)
# find bright pixel values that indicate frame and set to bw
thres = cv2.threshold(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), brightness_thres_low, brightness_thres_high, cv2.THRESH_BINARY_INV)[1]
thres = np.invert(thres)
# adapt image to only contain frame
kernel = np.ones((kernel_size, kernel_size), np.uint8)
img_adapted = cv2.erode(thres,kernel, iterations = erode_it_1)
img_adapted = cv2.dilate(img_adapted,kernel, iterations = dilate_it_1)
img_adapted = cv2.erode(img_adapted, kernel, iterations = erode_it_2)
If the frame is fully in the image, it gets detected fine. But as you can see in the example rgb image, part of the frame is covered by a plant. This results in an incomplete detection of the frame. I would like to fill this gap in the frame in a generic way such that it can be applied to any gap of any processed rgb image.
Here is some code you can start with:
import numpy as np
import cv2
kernel_size = 10
erode = 2
thres_low = 175
thres_high = 255
img = cv2.imread("img.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thres = cv2.threshold(gray, thres_low, thres_high, cv2.THRESH_BINARY)
kernel = np.ones((kernel_size, kernel_size), np.uint8)
thres = cv2.erode(thres, kernel, iterations=erode) # erode fine contours
contours, _ = cv2.findContours(thres, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # find contours
all_contours = np.vstack(contours) # concatenate all contours
hull = cv2.convexHull(all_contours) # get convex hull of all contours
peri = cv2.arcLength(hull, True) # get perimeter length of hull for the next step
approx = cv2.approxPolyDP(hull, 0.05 * peri, True) # simplify hull
cv2.drawContours(img, [approx], 0, (0, 0, 255), 10) # draw the contour
cv2.imwrite("out.png", img)
Output image:

OpenCV Contour Detection in color

I'm trying to detect the optic disc in an image of the back of the eye using OpenCV and findContour, then fitEllipse, but my program isn't detecting the optic disc at all. How do I fix this? Code and images are below
import cv2
import numpy as np
from sklearn.linear_model import LinearRegression
import math
from decimal import Decimal
def find_elongation(image):
img = cv2.imread(image,0)
ret,thresh = cv2.threshold(img,127,255,0)
contour,hierarchy = cv2.findContours(thresh, 1, 2)
contours = []
for i in range(len(contour)):
if len(contour[i])>=5:
contours.append(contour[i])
cnt = contours[0]
k = cv2.isContourConvex(cnt)
ellipse = cv2.fitEllipse(cnt)
im = cv2.ellipse(img,ellipse,(0,255,0),2)
(x,y),(ma,Ma),angle = cv2.fitEllipse(cnt)
return Ma/ma
print(find_elongation('eye.png'))
print(find_elongation('eye2.png'))
print(find_elongation('eye3.png'))
Image (one of them):
I'm trying to get the brightly colored circle in the middle:
Thanks for the help!
I have developed a piece of code to implement what you have asked. It mainly uses de Value channel of the HSV color space followed by some morphological operations.
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Read the image
img = cv2.imread('so.png')
# Transform the image to HSV color-space and keep only the value channel
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h,s,v = cv2.split(hsv)
# Threshold the iamge with the 95% of the brightest possible pixels
maxVal = np.max(v)
per = 0.95
_, th = cv2.threshold(v, maxVal*per, 255, cv2.THRESH_BINARY)
# Erode the image and find the connected components
th = cv2.erode(th, np.ones((2,2), np.uint8))
n, conComp, stats, centroids = cv2.connectedComponentsWithStats(th)
# Obtain the sizes of the connectedComponents skipping the background
sizes = stats[1:,-1]
# Obtain the number of the connectedComponent with biggest size
nComp = np.argmax(sizes) + 1
# Isolate the connectedComponent and apply closing
out = np.zeros((img.shape[0], img.shape[1]), np.uint8)
out[conComp==nComp] = 1
out = cv2.morphologyEx(out, cv2.MORPH_CLOSE, np.ones((10,10)))
# Apply gradient to mask to obtain the border of the ellipse
out = cv2.morphologyEx(out, cv2.MORPH_GRADIENT, np.ones((2,2)))
# Join the border of the ellipse with the image to display it
img[out==1] = (0,0,0)
plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
plt.show()
I attach the output I have obtained with the picture you posted:

Drawing circles around cells after watershed segmentation - openCV/Python

I followed a tutorial on watershed segmentation and used it to segment each red blood cell in an image. I'm new to openCV and I would like to know if it is possible to draw circles around the cells by using watershed segmentation? If so, could you please show how it is done.
Original image
Output of Watershed segmentation
Code is given below
import numpy as np
import cv2
from matplotlib import pyplot as plt
def fillHoles(otsuImg):
# find contours
contours, _ = cv2.findContours(otsuImg, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# filter out contours by size
small_cntrs = []
for con in contours:
area = cv2.contourArea(con)
# print(area)
if area < 1000: # size threshold
small_cntrs.append(con)
cv2.drawContours(otsuImg, small_cntrs, -1, 0, -1)
# load the image
img = cv2.imread('resources/rbc2.png')
img_pyr = cv2.pyrMeanShiftFiltering(img, 21, 51)
img_median = cv2.medianBlur(img_pyr, 9)
img_gray = cv2.cvtColor(img_median, cv2.COLOR_BGR2GRAY)
ret, img_thresh = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# fill holes of RBC
fillHoles(img_thresh)
# invert the image
img_thresh = cv2.bitwise_not(img_thresh)
# noise removal
kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(img_thresh,cv2.MORPH_OPEN,kernel, iterations=2)
# sure background area
sure_bg = cv2.dilate(opening,kernel,iterations=3)
# Finding sure foreground area
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2, 5)
ret, sure_fg = cv2.threshold(dist_transform,0.1*dist_transform.max(),255,0)
# _, sure_fg = cv2.threshold(np.uint8(dist_transform), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg, sure_fg)
# Marker labelling
ret, markers = cv2.connectedComponents(sure_fg)
# Add one to all labels so that sure background is not 0, but 1
markers = markers+1
# Now, mark the region of unknown with zero
markers[unknown==255] = 0
markers = cv2.watershed(img,markers)
img[markers == -1] = [255,0,0]
cv2.imshow('markers2', np.uint8(markers))
cv2.imshow('Final output', img)
cv2.waitKey(0)
I had the same problem and ended up using skimage.segmentation.watershed for the last step, to get the labels I could use to calculate the contours of the watershed cells. Once you have the contours, you can calculate and plot the enclosing circle as usual:
# your code above
ret, markers = cv2.connectedComponents(sure_fg)
from skimage.segmentation import watershed
labels = watershed(-dist_transform,
markers,
mask=img_thresh,
watershed_line=False)
watershed_contours = list(map(lambda l: cv2.findContours((labels == l).astype(np.uint8),
cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0][0],
np.unique(labels)[1:]))
output = img.copy()
for contour in watershed_contours:
(x,y),radius = cv2.minEnclosingCircle(contour)
center = (int(x),int(y))
radius = int(radius)
cv2.circle(output,center,radius,(0,255,0),2)

Extract only the t-shirt from an image and remove all other noise using openCV in Python

I want to extract a t-shirt from an image and remove all other noise. I have been trying to use the Watershed Algorithm in Python (openCV), however I can't seem to filter for only the t-shirt. Am I using the right algorithm for this or should I be looking at something else entirely?
My next step would be to find a way to calculate the measurements of the t-shirt.
Thanks in advance!
Image I am using
import cv2
import numpy as np
def display(img, cmap='gray'):
-->fig = plt.figure(figsize=(12,10))
-->ax = fig.add_subplot(111)
-->ax.imshow(img,cmap='gray')
#read in image --> added hyperlink to image
img = cv2.imread("..")
#blur and grayscale img
img = cv2.medianBlur(img, 5)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#apply threshold to grayscale image + otsu's method
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
#NOISE REMOVAL
kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN, kernel, iterations=6)
#set seeds to distinguish between fore and background
sure_bg = cv2.dilate(opening, kernel, iterations = 2)
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2,5)
#foreground
ret, sure_fg = cv2.threshold(dist_transform,0.80*dist_transform.max(),255,0)
#find region between shade and core white colours
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg,sure_fg)
#create label markers for watershed algorithm to make distinction between upper and lower body
ret, markers = cv2.connectedComponents(sure_fg)
markers = markers + 1
#set unknown equal to black
markers[unknown==255] = 0
#apply markers to algorithm
markers = cv2.watershed(img,markers)
image,contours,hierarchy = cv2.findContours(markers.copy(),cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
# create contours
for i in range(len(contours)):
-->#external contour
-->if hierarchy[0][i][3] == -1:
---->cv2.drawContours(img,contours,i,255,-1)

Separating cell boundaries in the following image and also do nuclear counting using python

I tried to use watershed with Otsu for thresholding but its only picking up nuclear boundaries,I want to segment cells boundaries
I used Otsu followed by noise removal with opening ,identifying sure background, applying distance transform, using it to define sure foreground, defining unknown, creating markers
import cv2
import numpy as np
img = cv2.imread("images/bio_watershed/Osteosarcoma_01.tif")
cells=img[:,:,0]
#Threshold image to binary using OTSU. ALl thresholded pixels will be set
#to 255
ret1, thresh = cv2.threshold(cells, 0, 255,
cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# Morphological operations to remove small noise - opening
kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel,
iterations = 2)
# finding sure background
sure_bg = cv2.dilate(opening,kernel,iterations=10)
#applying dustance transform
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5)
ret2, sure_fg
=cv2.threshold(dist_transform,0.5*dist_transform.max(),255,0)
# Unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg,sure_fg)
#Now we create a marker and label the regions inside.
ret3, markers = cv2.connectedComponents(sure_fg)
#add 10 to all labels so that sure background is not 0, but 10
markers = markers+10
# Now, mark the region of unknown with zero
markers[unknown==255] = 0
#applying watershed
markers = cv2.watershed(img,markers)
# color boundaries in yellow.
img[markers == -1] = [0,255,255]
img2 = color.label2rgb(markers, bg_label=0)
cv2.imshow('Overlay on original image', img)
cv2.imshow('Colored Cells', img2)
cv2.waitKey(0)
by running this code I get following nuclear boundary segmentation but I want to get the cell boundaries
Thanks so much for your help
I'm not sure if you are still looking for an answer but I have edited your code to segment the cell boundaries. You need to select the image slice that shows the actin filaments, which is in index 1.
I have also used an edge detector followed by contour drawing to outline the cell boundaries.
Here is my code:
import cv2
import numpy as np
import skimage.io as skio
img = cv2.imread("cells.png")
img_read = img[:,:,1] #Shows the actin filaments
#Denoising
img_denoise = cv2.fastNlMeansDenoising(img_read, 45, 7, 35)
#Canny edge detector
img_canny = cv2.Canny(img_denoise, 10, 200)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(2,2))
img_dilate = cv2.dilate(img_canny, kernel, iterations = 2)
#Contour finding
contours, _ = cv2.findContours(img_dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
img_med = cv2.cvtColor(img_denoise, cv2.COLOR_GRAY2RGB)
img_final = cv2.drawContours(img_med, contours, -1, (0,128,128), 2, 4)
skio.imsave("img_output.tif", img_final)
cv2.imshow('Overlay on original image', img_final)
cv2.waitKey(0)
The example you have is good for color-based segmentation as it is (better resolution will improve the result though).
Contrast is good enough (and can be improved), so did a very quick test without using OpenCV (so no code to share).
Nuclear boundary:
Cell boundary:
Combined:
Or as a separate masks:
So I'd say it is all about delta E and proper segmentation.

Categories