Although I think this can be solved with a well placed binary threshold (as stated in another answer), adding an addition level of basic morphology should make it more robust to significantly dirtier images. (sorry it is in C++)
I used an arbitrary binary threshold to demonstrate the concept, but would suggest using a statistics based threshold (such as otsu method) if available.
To explain the code a little: threshold converts the grayscale image to binary. Opening removes external noise (any little strips, pieces, or pixel noise) left by the threshold op. Closing infills any internal holes. "opening" and "closing" are just names for combinations of dilates and erodes in specific order to achieve a desired effect without changing the underlying objects size/shape.
#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <Windows.h>
#include <string>
using namespace cv;
int main(int argc, char** argv)
{
//C:/Local Software/voyDICOM/resources/images/oXsnC.jpg
std::string fileName = "C:/Local Software/voyDICOM/resources/images/oXsnC.jpg";
Mat tempImage = imread(fileName, cv::IMREAD_GRAYSCALE);
Mat bwImg;
//binary thresh (both of these work, otsu just gets a "smarter" threshold value rather than a hardcoded one)
cv::threshold(tempImage, bwImg, 150, 255, cv::THRESH_BINARY);
//cv::threshold(tempImage, bwImg, 0, 255, cv::THRESH_OTSU);
Mat openedImage;
//opening
cv::erode(bwImg, openedImage, cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3)), cv::Point(-1, -1), 2);
cv::dilate(openedImage, openedImage, cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3)), cv::Point(-1, -1), 2);
Mat closedImg;
//closing
cv::dilate(openedImage, closedImg, cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3)),cv::Point(-1,-1),5);
cv::erode(closedImg, closedImg, cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3)), cv::Point(-1, -1), 5);
namedWindow("Original", WINDOW_AUTOSIZE);
imshow("Original", tempImage);
namedWindow("Thresh", WINDOW_AUTOSIZE);
imshow("Thresh", bwImg);
namedWindow("Opened", WINDOW_AUTOSIZE);
imshow("Opened", openedImage);
namedWindow("Closed", WINDOW_AUTOSIZE);
imshow("Closed", closedImg);
waitKey(0);
system("pause");
return 0;
}
Result:
