Description
I have a lung cancer CT scan image, that I want to segment and extract the cancerous areas from. I have used Open CV and Java.
I have the following image as input:
After segmentation with thresholding and watershed method, I get this result:
After that, I want to extract the cancerous area from the segmented image, so I have to remove all noise and other objects outside the region of interest (the cancerous nodule). So like shown in image below, I want to extract the cancerous nodule like this:
How can I achieve this in android using OpenCV?
I tried to implement my suggested solution. My answer is in C++, but the idea is simple, you should be able to implement it in Java. As I commented, the idea is to use morphology to get the blob of interest. Mainly, the erode operation. Let's see:
//Read input image:
std::string imagePath = "C://opencvImages//lungsImage.png";
cv::Mat imageInput= cv::imread( imagePath );
//Convert it to grayscale:
cv::Mat grayImg;
cv::cvtColor( imageInput, grayImg, cv::COLOR_BGR2GRAY );
The first step is to obtain a binary image. It seems you implemented Watershed segmentation. That's ok. I tried applying a simple adaptive thresholding with a big window (601
in size, for this case). It gave me good results:
//Get the binary image:
cv::adaptiveThreshold( grayImg, grayImg, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, 601, 10 );
This is the result you get:
Now, there are multiple blobs. However, I’ll be looking for the biggest blob, as that is where our target region of interest is located. Searching for the biggest blob in a binary image is a task I often carry out, so I have prepared a function for this. It is called findBiggestBlob
. I’ll present the function later. Check out the result you get after filtering out the smaller blobs:
//Get the biggest blob in the binary image
cv::Mat targetBlobs = findBiggestBlob( grayImg );
This is the result:
Now, simply apply morphology. First, an erode
operation. Use an ellipse
structuring element of size 5 x 5
with 4
iterations to detach the blob of interest:
//Apply erosion to the biggest blob mask;
cv::Mat morphKernel = cv::getStructuringElement( cv::MORPH_ELLIPSE, cv::Size(5, 5) );
int morphIterations = 4; // use 4 iterations
cv::morphologyEx( targetBlobs, targetBlobs, cv::MORPH_ERODE, morphKernel, cv::Point(-1,-1), morphIterations );
Check out the result, the blob of interest is now detached:
Now, the idea is simple. If we, again, extract the biggest blob of the image, we should end up with the lungs, free from the cancerous area. Then, subtract this image to the “detached” mask, we should end up with the blob of interest in one mask:
//Get the lungs image:
cv::Mat bigBlob = findBiggestBlob( targetBlobs );
You get this:
//Subtract the lungs from the first binary mask:
cv::Mat blobOfInterest = targetBlobs - bigBlob;
Now, let's restore the blob's original size applying a dilate
operation, use the same structuring element and the same number of iterations. This is the result:
//Restore the blob's original size:
cv::morphologyEx( blobOfInterest, blobOfInterest, cv::MORPH_DILATE, morphKernel, cv::Point(-1,-1), morphIterations );
Here's the blob (in red) overlaid onto the original image:
This is the code for the findBiggestBlob
function. The idea is just to compute all the contours in the binary input, calculate their area and store the contour with the largest area of the bunch:
//Function to get the largest blob in a binary image:
cv::Mat findBiggestBlob( cv::Mat &inputImage ){
cv::Mat biggestBlob = inputImage.clone();
int largest_area = 0;
int largest_contour_index=0;
std::vector< std::vector<cv::Point> > contours; // Vector for storing contour
std::vector<cv::Vec4i> hierarchy;
// Find the contours in the image
cv::findContours( biggestBlob, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
for( int i = 0; i< (int)contours.size(); i++ ) {
//Find the area of the contour
double a = cv::contourArea( contours[i],false);
//Store the index of largest contour:
if( a > largest_area ){
largest_area = a;
largest_contour_index = i;
}
}
//Once you get the biggest blob, paint it black:
cv::Mat tempMat = biggestBlob.clone();
cv::drawContours( tempMat, contours, largest_contour_index, cv::Scalar(0),
CV_FILLED, 8, hierarchy );
//Erase the smaller blobs:
biggestBlob = biggestBlob - tempMat;
tempMat.release();
return biggestBlob;
}