OpenCV — как получить лучший контур руки из некачественного серого изображения?

Мне нужно получить контур от изображения руки, обычно я обрабатываю изображение в 4 этапа:

  1. получить сырое RGB-серое изображение с 3 каналов на 1 канал:

    cvtColor(sourceGrayImage, sourceGrayImage, COLOR_BGR2GRAY);
    
  2. использовать размытие по Гауссу для фильтрации серого изображения:

    GaussianBlur(sourceGrayImage, sourceGrayImage, Size(3,3), 0);
    
  3. двоичное серое изображение, я делю изображение по высоте, обычно я делю изображение на 6 изображений по высоте, затем каждое делаю пороговый процесс:

    // we split source picture to binaryImageSectionCount(here it's 8) pieces by its height,
    // then we for every piece, we do threshold,
    // and at last we combine them agin to binaryImage
    const binaryImageSectionCount = 8;
    void GetBinaryImage(Mat &grayImage, Mat &binaryImage)
    {
    // get every partial gray image's height
    int partImageHeight = grayImage.rows / binaryImageSectionCount;
    for (int i = 0; i < binaryImageSectionCount; i++)
    {
    Mat partialGrayImage;
    Mat partialBinaryImage;
    Rect partialRect;
    if (i != binaryImageSectionCount - 1)
    {
    // if it's not last piece, Rect's height should be partImageHeight
    partialRect = Rect(0, i * partImageHeight, grayImage.cols, partImageHeight);
    }
    else
    {
    // if it's last piece, Rect's height should be (grayImage.rows - i  * partImageHeight)
    partialRect = Rect(0, i * partImageHeight, grayImage.cols, grayImage.rows - i  * partImageHeight);
    }
    
    Mat partialResource = grayImage(partialRect);
    partialResource.copyTo(partialGrayImage);
    threshold( partialGrayImage, partialBinaryImage, 0, 255, THRESH_OTSU);
    
    // combin partial binary image to one piece
    partialBinaryImage.copyTo(binaryImage(partialRect));
    
    ///*stringstream resultStrm;
    //resultStrm << "partial_" << (i + 1);
    //string string = resultStrm.str();
    
    //imshow(string, partialBinaryImage);
    //waitKey(0);*/
    }
    imshow("result binary image.", binaryImage);
    waitKey(0);
    return;
    }
    
  4. используйте findcontour, чтобы получить самый большой контур области:

    vector<vector<Point> > contours;
    findContours(binaryImage, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
    

обычно это работает хорошо,
Но для некачественного серого изображения оно не работает, как показано ниже:

LowQualityGrayImage1

LowQualityGrayImage2

полный код здесь:

#include <opencv2/imgproc/imgproc.hpp>
#include<opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace std;
using namespace cv;// we split source picture to binaryImageSectionCount(here it's 8) pieces by its height,
// then we for every piece, we do threshold,
// and at last we combine them agin to binaryImage
const binaryImageSectionCount = 8;
void GetBinaryImage(Mat &grayImage, Mat &binaryImage)
{
// get every partial gray image's height
int partImageHeight = grayImage.rows / binaryImageSectionCount;
for (int i = 0; i < binaryImageSectionCount; i++)
{
Mat partialGrayImage;
Mat partialBinaryImage;
Rect partialRect;
if (i != binaryImageSectionCount - 1)
{
// if it's not last piece, Rect's height should be partImageHeight
partialRect = Rect(0, i * partImageHeight, grayImage.cols, partImageHeight);
}
else
{
// if it's last piece, Rect's height should be (grayImage.rows - i  * partImageHeight)
partialRect = Rect(0, i * partImageHeight, grayImage.cols, grayImage.rows - i  * partImageHeight);
}

Mat partialResource = grayImage(partialRect);
partialResource.copyTo(partialGrayImage);
threshold( partialGrayImage, partialBinaryImage, 0, 255, THRESH_OTSU);

// combin partial binary image to one piece
partialBinaryImage.copyTo(binaryImage(partialRect));

///*stringstream resultStrm;
//resultStrm << "partial_" << (i + 1);
//string string = resultStrm.str();

//imshow(string, partialBinaryImage);
//waitKey(0);*/
}
imshow("result binary image.", binaryImage);
waitKey(0);
return;
}int main(int argc, _TCHAR* argv[])
{
// get image path
string imgPath("C:\\Users\\Alfred\\Desktop\\gray.bmp");

// read image
Mat src = imread(imgPath);
imshow("Source", src);
//medianBlur(src, src, 7);
cvtColor(src, src, COLOR_BGR2GRAY);
imshow("gray", src);

// do filter
GaussianBlur(src, src, Size(3,3), 0);

// binary image
Mat threshold_output(src.rows, src.cols, CV_8UC1, Scalar(0, 0, 0));
GetBinaryImage(src, threshold_output);
imshow("binaryImage", threshold_output);

// get biggest contour
vector<vector<Point> > contours;
findContours(threshold_output,contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
int biggestContourIndex = 0;
int maxContourArea = -1000;
for (int i = 0; i < contours.size(); i++)
{
if (contourArea(contours[i]) > maxContourArea)
{
maxContourArea = contourArea(contours[i]);
biggestContourIndex = i;
}
}

// show biggest contour
Mat biggestContour(threshold_output.rows, threshold_output.cols, CV_8UC1, Scalar(0, 0, 0));
drawContours(biggestContour, contours, biggestContourIndex, cv::Scalar(255,255,255), 2, 8, vector<Vec4i>(), 0, Point());
imshow("maxContour", biggestContour);
waitKey(0);

}

Кто-нибудь может помочь мне получить лучший результат контура руки?
Спасибо!!!

2

Решение

У меня есть фрагмент кода в Python, вы можете использовать тот же подход в C:

img = cv2.imread(x, 1)
cv2.imshow("img",img)

imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow("gray",imgray)

#Code for histogram equalization
equ = cv2.equalizeHist(imgray)
cv2.imshow('equ', equ)

#Code for contrast limited adaptive histogram equalization
#clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
#cl2 = clahe.apply(imgray)
#cv2.imshow('clahe2', cl2)

Вот результат, который я получил:

введите описание изображения здесь

Если ваше изображение ужасно плохое, вы можете попробовать код, который я прокомментировал, с использованием адаптивной эквализации гистограммы с ограниченной контрастностью.

1

Другие решения

Других решений пока нет …