Оттенок Otsu для изображения глубины
Я пытаюсь выставить фон из изображений глубины, полученных с помощью kinect. Когда я узнал, что такое отсуждение, я думал, что это возможно. Преобразование изображения глубины в оттенки серого я могу надеяться применить порог otsu для бинаризации изображения.
Однако я реализовал (попытался реализовать) это с OpenCV 2.3, он пришел напрасно. Выходное изображение бинаризовано, однако, очень неожиданно. Я сделал пороговое значение непрерывно (т.е. Распечатал результат на экране для анализа для каждого кадра) и увидел, что для некоторых кадров пороговое значение установлено равным 160, а иногда оно оказывается равным 0. Я не мог понять, почему это происходит. Возможно, это связано с большим количеством 0 в изображении глубины, возвращаемом kinect, что соответствует пикселам, которые невозможно измерить. Есть ли способ, которым я мог бы сказать алгоритму игнорировать пиксели, имеющие значение 0? Или порог отсу не подходит для того, что я пытаюсь сделать?
Вот несколько выходов и сегментов связанного кода. Вы можете заметить, что второй скриншот выглядит так, как будто он может сделать хорошую бинаризацию, однако я хочу достичь той, которая отличает различия между пикселями, соответствующими стулу в сцене и backgroung.
Спасибо.
cv::Mat1s depthcv(depth->getHeight(), depth->getWidth());
cv::Mat1b depthcv8(depth->getHeight(), depth->getWidth());
cv::Mat1b depthcv8_th(depth->getHeight(), depth->getWidth());
depthcv.data =(uchar*) depth->getDepthMetaData().Data();
depthcv.convertTo(depthcv8,CV_8U,255/5000.f);
//apply otsu thresholding
cv::threshold(depthcv8, depthcv8_th, 128, 255, CV_THRESH_BINARY|CV_THRESH_OTSU);
std::ofstream output;
output.open("output.txt");
//output << "M = "<< endl << " " << depthcv8 << endl << endl;
cv::imshow("lab",depthcv8_th);
cv::waitKey(1);
![Image 1]()
![this second screenshot looks like it could do some good binarization]()
Ответы
Ответ 1
Otsu, вероятно, достаточно хорош для того, что вы пытаетесь сделать, но вам нужно замаскировать нулевые значения перед вычислением оптимального порога с помощью алгоритма Otsu, иначе распределение значений интенсивности будет искажено ниже, чем вы хотите.
OpenCV не предоставляет аргумент маски для функции cv::threshold
, поэтому вам придется самостоятельно удалить эти значения. Я бы рекомендовал поместить все ненулевые значения в матрицу 1 на N и вызвать функцию cv::threshold
с помощью CV_THRESH_OTSU
и сохранить возвращаемое значение (которое является оценочным оптимальным порогом), а затем запустить функцию cv::threshold
снова на исходном изображении с помощью только флага CV_THRESH_BINARY
и вычисленного порога.
Вот одна из возможных реализаций:
// move zeros to the back of a temp array
cv::Mat copyImg = origImg;
uint8* ptr = copyImg.datastart;
uint8* ptr_end = copyImg.dataend;
while (ptr < ptr_end) {
if (*ptr == 0) { // swap if zero
uint8 tmp = *ptr_end;
*ptr_end = *ptr;
*ptr = tmp;
ptr_end--; // make array smaller
} else {
ptr++;
}
}
// make a new matrix with only valid data
cv::Mat nz = cv::Mat(std::vector<uint8>(copyImg.datastart,ptr_end),true);
// compute optimal Otsu threshold
double thresh = cv::threshold(nz,nz,0,255,CV_THRESH_BINARY | CV_THRESH_OTSU);
// apply threshold
cv::threshold(origImg,origImg,thresh,255,CV_THRESH_BINARY_INV);