Extract hand bones from X-ray image
Asked Answered
J

1

8

I have x-ray image of a hand. I need to extract bones automatically. I can easily segmentate a hand using different techniques. But I need to get bones and using those techniques don't help. Some of the bones are brighter then orthers, so if I use thresholding some of them disapear while others become clearer rising threshold. And I think maybe I should threshold a region of the hand only? Is it possible to threshold ROI that is not a square? O maybe you have any other solutions, advices? Maybe there are some libraries like OpenCV or something for that? Any help would be very great!

Extended:

Raw Image Expected Output

                 Raw Image                      Expected Output

Jollity answered 30/3, 2013 at 12:3 Comment(4)
Are you sure that this relates to processing?Funch
There are great grad school programs in computer vision.Chevrette
opencv has an adaptiveThreshold method, also a distance transform in front of the threshold might helpChill
Add a sample image to your question and show us what you are trying to do.Hillis
H
10

One approach could be to segment the hand and fingers from the image:

enter image description here

And then creating another image with just the hand silhouette:

enter image description here

Once you have the silhouette you can erode the image to make it a little smaller. This is used to subtract the hand from the hand & fingers image, resulting in the fingers:

enter image description here

The code below shows to execute this approach:

void detect_hand_and_fingers(cv::Mat& src);
void detect_hand_silhoutte(cv::Mat& src);

int main(int argc, char* argv[])
{
    cv::Mat img = cv::imread(argv[1]);
    if (img.empty())
    {
        std::cout << "!!! imread() failed to open target image" << std::endl;
        return -1;        
    }

    // Convert RGB Mat to GRAY
    cv::Mat gray;
    cv::cvtColor(img, gray, CV_BGR2GRAY);
    cv::Mat gray_silhouette = gray.clone();

    /* Isolate Hand + Fingers */

    detect_hand_and_fingers(gray);
    cv::imshow("Hand+Fingers", gray);
    cv::imwrite("hand_fingers.png", gray);

    /* Isolate Hand Sillhoute and subtract it from the other image (Hand+Fingers) */

    detect_hand_silhoutte(gray_silhouette);
    cv::imshow("Hand", gray_silhouette);
    cv::imwrite("hand_silhoutte.png", gray_silhouette);

    /* Subtract Hand Silhoutte from Hand+Fingers so we get only Fingers */

    cv::Mat fingers =  gray - gray_silhouette;
    cv::imshow("Fingers", fingers);
    cv::imwrite("fingers_only.png", fingers);
    cv::waitKey(0);

    return 0;
}

void detect_hand_and_fingers(cv::Mat& src)
{        
    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3,3), cv::Point(1,1));
    cv::morphologyEx(src, src, cv::MORPH_ELLIPSE, kernel);    

    int adaptiveMethod = CV_ADAPTIVE_THRESH_GAUSSIAN_C; // CV_ADAPTIVE_THRESH_MEAN_C, CV_ADAPTIVE_THRESH_GAUSSIAN_C
    cv::adaptiveThreshold(src, src, 255, 
                          adaptiveMethod, CV_THRESH_BINARY, 
                          9, -5);

    int dilate_sz = 1;
    cv::Mat element = cv::getStructuringElement(cv::MORPH_ELLIPSE,
                                       cv::Size(2*dilate_sz, 2*dilate_sz),
                                       cv::Point(dilate_sz, dilate_sz) );
    cv::dilate(src, src, element);
}

void detect_hand_silhoutte(cv::Mat& src)
{
    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(7, 7), cv::Point(3, 3));
    cv::morphologyEx(src, src, cv::MORPH_ELLIPSE, kernel);        

    int adaptiveMethod = CV_ADAPTIVE_THRESH_MEAN_C; // CV_ADAPTIVE_THRESH_MEAN_C, CV_ADAPTIVE_THRESH_GAUSSIAN_C
    cv::adaptiveThreshold(src, src, 255, 
                          adaptiveMethod, CV_THRESH_BINARY, 
                          251, 5); // 251, 5

    int erode_sz = 5;
    cv::Mat element = cv::getStructuringElement(cv::MORPH_ELLIPSE,
                                       cv::Size(2*erode_sz + 1, 2*erode_sz+1),
                                       cv::Point(erode_sz, erode_sz) );
    cv::erode(src, src, element);

    int dilate_sz = 1;
    element = cv::getStructuringElement(cv::MORPH_ELLIPSE,
                                       cv::Size(2*dilate_sz + 1, 2*dilate_sz+1),
                                       cv::Point(dilate_sz, dilate_sz) );
    cv::dilate(src, src, element);

    cv::bitwise_not(src, src);
}
Hillis answered 31/3, 2013 at 3:40 Comment(3)
Can I ask which OpenCV version are you using for this demo code?Retiform
This answer is from 2013. At the time two people knew the answer. Today, only God knows.Hillis
Thanks for answering. I've made it work with a OpenCV 3.1.0 nuget package: nuget.org/packages/opencv.win.native/310.3.0, using Visual Studio 2015.Retiform

© 2022 - 2024 — McMap. All rights reserved.