0

I have a problem where I am struggling to make a purely binary mask of the lungs, where the pixel value is one inside of the lung and is a 1 outside of the lungs. I used a kmeans and otsu and a few other methods to segment the lungs. I will attach a few example pictures.

First Example

Second example, same patient/CT. I have no idea why this one has a circle around it

Here is a link for a 3d numpy array. It is of all the slices, so you will want to just maybe try out one slice.

https://drive.google.com/file/d/1nktGBYZGz1iJDR_-yarzlRs-c4xOp__9/view?usp=sharing

As you can see, the lung is segmented well. (It is white in the middle of the pictures). Is there any way for me to identify that middle white blob (the lung) and make every pixel outside of it black (0?) I'd appreciate the help very much if someone could guide me.

Here is the code I used to segment the lung (making a binary mask):

def HUValueSegmentation(image, fill_lung_structures=True):

# not actually binary, but 1 and 2. 
# 0 is treated as background, which we do not want
binary_image = np.array(image > -320, dtype=np.int8)+1
labels = measure.label(binary_image)

# Pick the pixel in the very corner to determine which label is air.
#   Improvement: Pick multiple background labels from around the patient
#   More resistant to "trays" on which the patient lays cutting the air 
#   around the person in half
background_label = labels[0,0,0]

#Fill the air around the person
binary_image[background_label == labels] = 2


# Method of filling the lung structures (that is superior to something like 
# morphological closing)
if fill_lung_structures:
    # For every slice we determine the largest solid structure
    for i, axial_slice in enumerate(binary_image):
        axial_slice = axial_slice - 1
        labeling = measure.label(axial_slice)
        l_max = largest_label_volume(labeling, bg=0)
        
        if l_max is not None: #This slice contains some lung
            binary_image[i][labeling != l_max] = 1


binary_image -= 1 #Make the image actual binary
binary_image = 1-binary_image # Invert it, lungs are now 1

# Remove other air pockets insided body
labels = measure.label(binary_image, background=0)
l_max = largest_label_volume(labels, bg=0)
if l_max is not None: # There are air pockets
    binary_image[labels != l_max] = 0

return binary_image
Mattias Weiland
  • 61
  • 1
  • 2
  • 6

1 Answers1

1

Since the lungs are in the middle of a large negative area on the mask I filtered out the rest of the mask by doing a bitwise_and of the area inside the largest negative area in the image.

enter image description here

enter image description here

Edit: I didn't change the main body of the code at all, but I modified it to take in a numpy array as a series of images.

enter image description here

import cv2
import numpy as np

# load numpy array
images = np.load("array.npy");

# do the lung thing
counter = 0;
for img in images:
    # convert to uint8
    img *= 255;
    inty = img.astype(np.uint8);

    # dilate
    kernel = np.ones((3,3), np.uint8);
    mask = cv2.dilate(inty, kernel, iterations = 1);

    # invert
    mask = cv2.bitwise_not(mask);

    # contours # OpenCV 3.4, this returns (contours, _) on OpenCV 2 and 4
    _, contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);

    # find biggest
    biggest = None;
    big_size = -1;
    for con in contours:
        area = cv2.contourArea(con);
        if area > big_size:
            big_size = area;
            biggest = con;

    # draw fill mask
    mask2 = np.zeros_like(mask);
    cv2.drawContours(mask2, [biggest], -1, (255), -1);

    # combine
    lungs_mask = cv2.bitwise_and(inty, mask2);

    # show
    cv2.imshow("Lungs", inty);
    cv2.imshow("Mask", lungs_mask);
    cv2.waitKey(30);
Ian Chu
  • 2,924
  • 9
  • 14
  • Thank you so much. This is a 3d volume, so I understand that I will need to do this slice by slice. I have a numpy 3d array that I will iterate through slice by slice and try to use your method. However, you did this method just from the photo I posted(as your input data). I am getting many errors, as you did your method from a colored png photo. Do you understand my problem? I would think that my numpy array is just like the variable 'gray' (top of your code) but nothing seems to work out when I have tried many things. – Mattias Weiland Feb 27 '21 at 00:47
  • can you post the numpy array so that I can try it out? – Ian Chu Feb 27 '21 at 01:11
  • Do you want me to actually post all 512x512 values for a single slice? or is there an easier way to upload a numpy array? – Mattias Weiland Feb 27 '21 at 01:29
  • You can use numpy.save(array) to save the array as a file. – Ian Chu Feb 27 '21 at 01:30
  • Hm, I am not sure where to upload the saved npy file to stackoverflow. I can only see an option for pictures. Can I email it to you? – Mattias Weiland Feb 27 '21 at 01:40
  • If you have a google drive, or any other hosting site, you can share the link in you question description. – Ian Chu Feb 27 '21 at 01:48
  • Just put the link in the question. Note that it is the array for the whole 130 slices of the CT scan. (3D). Thank you so much. – Mattias Weiland Feb 27 '21 at 01:55