2016-01-12 2 views
17

Мы фактически работаем над проектом анализа изображений, где нам нужно идентифицировать объекты, которые исчезли/появились в сцене. Вот 2 изображения, один из которых был снят до того, как хирург и другие впоследствии сделали операцию.Оптический поток игнорирует разреженные движения

ДО: enter image description here ПОСЛЕ: enter image description here

Во-первых, мы просто вычислили разницу между 2 изображения и вот результат (Обратите внимание, что я добавил 128 в результате Mat просто иметь более хорошее изображение) :

(ПОСЛЕ - дО) + 128 enter image description here

цель состоит в том, чтобы обнаружить, что чашка (красная стрелка) исчезла из SCE ne и шприц (черная стрелка) вошли в сцену, другими словами, мы должны обнаружить ТОЛЬКО области, которые соответствуют объектам, оставленным/введенным в сцену. Кроме того, очевидно, что объекты в левом верхнем углу сцены немного сдвинулись с начальной позиции. Я думал о Optical flow так что я использовал OpenCV C++ для расчета один на Farneback для того, чтобы увидеть, если это достаточно для нашего случая и вот результат мы получили, а затем код, который мы писали:

FLOW: enter image description here

void drawOptFlowMap(const Mat& flow, Mat& cflowmap, int step, double, const Scalar& color) 
{ 
    cout << flow.channels() << "/" << flow.rows << "/" << flow.cols << endl; 
    for(int y = 0; y < cflowmap.rows; y += step) 
     for(int x = 0; x < cflowmap.cols; x += step) 
     { 
      const Point2f& fxy = flow.at<Point2f>(y, x); 
      line(cflowmap, Point(x,y), Point(cvRound(x+fxy.x), cvRound(y+fxy.y)), color); 
      circle(cflowmap, Point(x,y), 1, color, -1); 
     } 
} 

void MainProcessorTrackingObjects::diffBetweenImagesToTestTrackObject(string pathOfImageCaptured, string pathOfImagesAfterOneAction, string pathOfResultsFolder) 
{ 
    //Preprocessing step... 

    string pathOfImageBefore = StringUtils::concat(pathOfImageCaptured, imageCapturedFileName); 
    string pathOfImageAfter = StringUtils::concat(pathOfImagesAfterOneAction, *it); 

    Mat imageBefore = imread(pathOfImageBefore); 
    Mat imageAfter = imread(pathOfImageAfter); 

    Mat imageResult = (imageAfter - imageBefore) + 128; 
    //   absdiff(imageAfter, imageBefore, imageResult); 
    string imageResultPath = StringUtils::stringFormat("%s%s-color.png",pathOfResultsFolder.c_str(), fileNameWithoutFrameIndex.c_str()); 
    imwrite(imageResultPath, imageResult); 

    Mat imageBeforeGray, imageAfterGray; 
    cvtColor(imageBefore, imageBeforeGray, CV_RGB2GRAY); 
    cvtColor(imageAfter, imageAfterGray, CV_RGB2GRAY); 

    Mat imageResultGray = (imageAfterGray - imageBeforeGray) + 128; 
    //   absdiff(imageAfterGray, imageBeforeGray, imageResultGray); 
    string imageResultGrayPath = StringUtils::stringFormat("%s%s-gray.png",pathOfResultsFolder.c_str(), fileNameWithoutFrameIndex.c_str()); 
    imwrite(imageResultGrayPath, imageResultGray); 


    //*** Compute FarneBack optical flow 
    Mat opticalFlow; 
    calcOpticalFlowFarneback(imageBeforeGray, imageAfterGray, opticalFlow, 0.5, 3, 15, 3, 5, 1.2, 0); 

    drawOptFlowMap(opticalFlow, imageBefore, 5, 1.5, Scalar(0, 255, 255)); 
    string flowPath = StringUtils::stringFormat("%s%s-flow.png",pathOfResultsFolder.c_str(), fileNameWithoutFrameIndex.c_str()); 
    imwrite(flowPath, imageBefore); 

    break; 
} 

И знать, насколько точны этот оптический поток, я написал этот небольшой фрагмент кода, который вычисляет (IMAGEAFTER + FLOW) - IMAGEBEFORE:

//Reference method just to see the accuracy of the optical flow calculation 
Mat accuracy = Mat::zeros(imageBeforeGray.rows, imageBeforeGray.cols, imageBeforeGray.type()); 

strinfor(int y = 0; y < imageAfter.rows; y ++) 
for(int x = 0; x < imageAfter.cols; x ++) 
{ 
    Point2f& fxy = opticalFlow.at<Point2f>(y, x); 
    uchar intensityPointCalculated = imageAfterGray.at<uchar>(cvRound(y+fxy.y), cvRound(x+fxy.x)); 
    uchar intensityPointBefore = imageBeforeGray.at<uchar>(y,x); 
    uchar intensityResult = ((intensityPointCalculated - intensityPointBefore)/2) + 128; 
    accuracy.at<uchar>(y, x) = intensityResult; 
} 
validationPixelBased = StringUtils::stringFormat("%s%s-validationPixelBased.png",pathOfResultsFolder.c_str(), fileNameWithoutFrameIndex.c_str()); 
imwrite(validationPixelBased, accuracy); 

намерение, имеющих т его ((intensityPointCalculated - intensityPointBefore)/2) + 128; просто для того, чтобы иметь приемлемое изображение.

IMAGE РЕЗУЛЬТАТ:

enter image description here

Поскольку он обнаруживает все регионы, которые были сдвинуты/Введенный/покинул сцену, мы считаем, что OpticalFlow не достаточно, чтобы обнаружить только регионы, представляющие объекты исчезли/появился на сцене. Есть ли способ игнорировать разреженные движения, обнаруженные opticalFlow? Или есть альтернативный способ обнаружить то, что нам нужно?

ответ

9

Предположим, цель состоит в том, чтобы идентифицировать области с появляющимися/исчезнувшими объектами, но не те, которые присутствуют на обеих картинках, а просто перемещены.

Оптический поток должен быть хорошим способом, как вы уже сделали. Однако проблема заключается в том, как оценивается результат. В отличие от различий между пикселями и пикселями, которые не имеют толерантности к отклонениям от поворота/масштабирования, вы можете выполнить сопоставление функций (SIFT и т. Д. Check out here for what you can use with opencv)

Вот что я получаю с хорошими функциями для отслеживания с вашего изображения раньше.

GoodFeaturesToTrackDetector detector; 
vector<KeyPoint> keyPoints; 
vector<Point2f> kpBefore, kpAfter; 
detector.detect(imageBefore, keyPoints); 

enter image description here

Вместо плотного оптического потока, можно использовать разреженный поток и отслеживать только особенности,

vector<uchar> featuresFound; 
vector<float> err; 
calcOpticalFlowPyrLK(imageBeforeGray, imageAfterGray, keyPointsBefore, keyPointsAfter, featuresFound, err, Size(PATCH_SIZE , PATCH_SIZE)); 

Выход включает в себя значения FeaturesFound и ошибок. Я просто использовал здесь порог, чтобы отличить перемещенные функции и непревзойденные исчезнувшие.

vector<KeyPoint> kpNotMatched; 
for (int i = 0; i < kpBefore.size(); i++) { 
    if (!featuresFound[i] || err[i] > ERROR_THRESHOLD) { 
     kpNotMatched.push_back(KeyPoint(kpBefore[i], 1)); 
    } 
} 
Mat output; 
drawKeypoints(imageBefore, kpNotMatched, output, Scalar(0, 0, 255)); 

enter image description here

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

Mat mask = Mat::zeros(imageBefore.rows, imageBefore.cols, CV_8UC1); 
for (int i = 0; i < kpNotMatched.size(); i++) { 
    mask.at<uchar>(kpNotMatched[i].pt) = 255; 
} 
blur(mask, mask, Size(BLUR_SIZE, BLUR_SIZE)); 
threshold(mask, mask, MASK_THRESHOLD, 255, THRESH_BINARY); 

enter image description here

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

vector<vector<Point> > contours; 
vector<Vec4i> hierarchy; 
findContours(mask, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0)); 

vector<vector<Point> >hull(contours.size()); 
for(int i = 0; i < contours.size(); i++) { 
    convexHull(Mat(contours[i]), hull[i], false); 
} 
for(int i = 0; i < contours.size(); i++) { 
    drawContours(output, hull, i, Scalar(0, 255, 255), 3, 8, vector<Vec4i>(), 0, Point()); 
} 

enter image description here

И просто делать это в обратном порядке (от сопоставления imageAfter к imageBefore), чтобы получить появились регионы. :)

+0

Я не в состоянии воспроизвести те же результаты .. можете ли вы рассказать мне значения, которые вы используете для этих констант: BLUR_SIZE, ERROR_THRESHOLD, MASK_THRESHOLD – Maystro

+0

На основании входное изображение 960 x 540, у меня было BLUR_SIZE = 35, ERROR_THRESHOLD = 30, MASK_THRESHOLD = 1.5. Вы также можете настроить другие параметры, такие как разреженные уровни пирамиды оптического потока, размер патча и т. Д. Однако простое постоянное пороговое значение может не работать хорошо во всех ситуациях, и вы можете применить более сложные стратегии на основе ваших вариантов использования. – myin528

+0

Спасибо за вашу поддержку. Ваш ответ не охватывает большинство моих дел, но я дам вам щедрость, так как он достаточно близко. – Maystro

0

Вы можете попробовать двухсторонний подход. Использование метода разности изображений отлично подходит для обнаружения объектов, которые входят и выходят из сцены, если цвет объекта отличается от цвета фона. Меня поражает то, что было бы значительно улучшено, если бы вы могли удалить объекты, которые были перенесены перед использованием метода.

Существует отличный метод OpenCV для обнаружения объекта here, который находит достопримечательности в изображении для обнаружения перевода объекта.Я думаю, вы могли бы добиться того, что вы хотите с следующим методом -

-Сравнить изображения с кодом OpenCV и выделить двигающиеся объекты в обоих изображениях

2 Цвета в обнаруженных объектах с фоном другой картиной в том же наборе пиксели (или нечто подобное), чтобы уменьшить разницу в изображениях, которое вызвано двигающимися изображениями

3 Найти разницу изображения, которые должны теперь иметь большие крупные объекты и мелкие артефакты, оставшиеся от двигающихся изображений

4 Порога для определенного размера объекта, обнаруженного в изображение разница

5 Составьте список вероятных кандидатов

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

1

Вот что я пробовал;

  • Обнаружить регионы, которые претерпели изменения.Для этого я использую простое различие кадров, пороговое значение, морфологические операции и выпуклость.
  • Найдите в этих изображениях точки зрения этих регионов и посмотрите, совпадают ли они. Хорошее совпадение в регионе указывает на то, что оно не претерпело существенных изменений. Плохой матч означает, что две области теперь разные. Для этого я использую BOW и Bhattacharyya расстояние.

Параметры могут нуждаться в настройке. Я использовал значения, которые только что работали для двух образцовых изображений. В качестве детектора признаков/дескриптора я использовал SIFT (несвободный). Вы можете попробовать другие детекторы и дескрипторы.

различия Изображение: diff

Регионы: regions

Изменение (красная: вставка/удаление, Желтое: редкие движения): changes

// for non-free modules SIFT/SURF 
cv::initModule_nonfree(); 

Mat im1 = imread("1.png"); 
Mat im2 = imread("2.png"); 

// downsample 
/*pyrDown(im1, im1); 
pyrDown(im2, im2);*/ 

Mat disp = im1.clone() * .5 + im2.clone() * .5; 
Mat regions = Mat::zeros(im1.rows, im1.cols, CV_8U); 

// gray scale 
Mat gr1, gr2; 
cvtColor(im1, gr1, CV_BGR2GRAY); 
cvtColor(im2, gr2, CV_BGR2GRAY); 
// simple frame differencing 
Mat diff; 
absdiff(gr1, gr2, diff); 
// threshold the difference to obtain the regions having a change 
Mat bw; 
adaptiveThreshold(diff, bw, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY_INV, 15, 5); 
// some post processing 
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3)); 
morphologyEx(bw, bw, MORPH_CLOSE, kernel, Point(-1, -1), 4); 
// find contours in the change image 
Mat cont = bw.clone(); 
vector<vector<Point> > contours; 
vector<Vec4i> hierarchy; 
findContours(cont, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, Point(0, 0)); 
// feature detector, descriptor and matcher 
Ptr<FeatureDetector> featureDetector = FeatureDetector::create("SIFT"); 
Ptr<DescriptorExtractor> descExtractor = DescriptorExtractor::create("SIFT"); 
Ptr<DescriptorMatcher> descMatcher = DescriptorMatcher::create("FlannBased"); 

if(featureDetector.empty() || descExtractor.empty() || descMatcher.empty()) 
{ 
    cout << "featureDetector or descExtractor or descMatcher was not created" << endl; 
    exit(0); 
} 
// BOW 
Ptr<BOWImgDescriptorExtractor> bowExtractor = new BOWImgDescriptorExtractor(descExtractor, descMatcher); 

int vocabSize = 10; 
TermCriteria terminate_criterion; 
terminate_criterion.epsilon = FLT_EPSILON; 
BOWKMeansTrainer bowTrainer(vocabSize, terminate_criterion, 3, KMEANS_PP_CENTERS); 

Mat mask(bw.rows, bw.cols, CV_8U); 
for(size_t j = 0; j < contours.size(); j++) 
{ 
    // discard regions that a below a specific threshold 
    Rect rect = boundingRect(contours[j]); 
    if ((double)(rect.width * rect.height)/(bw.rows * bw.cols) < .01) 
    { 
     continue; // skip this region as it's too small 
    } 
    // prepare a mask for each region 
    mask.setTo(0); 
    vector<Point> hull; 
    convexHull(contours[j], hull); 
    fillConvexPoly(mask, hull, Scalar::all(255), 8, 0); 

    fillConvexPoly(regions, hull, Scalar::all(255), 8, 0); 

    // extract keypoints from the region 
    vector<KeyPoint> im1Keypoints, im2Keypoints; 
    featureDetector->detect(im1, im1Keypoints, mask); 
    featureDetector->detect(im2, im2Keypoints, mask); 
    // get their descriptors 
    Mat im1Descriptors, im2Descriptors; 
    descExtractor->compute(im1, im1Keypoints, im1Descriptors); 
    descExtractor->compute(im2, im2Keypoints, im2Descriptors); 

    if ((0 == im1Keypoints.size()) || (0 == im2Keypoints.size())) 
    { 
     // mark this contour as object arrival/removal region 
     drawContours(disp, contours, j, Scalar(0, 0, 255), 2); 
     continue; 
    } 

    // bag-of-visual-words 
    Mat vocabulary = bowTrainer.cluster(im1Descriptors); 
    bowExtractor->setVocabulary(vocabulary); 
    // get the distribution of visual words in the region for both images 
    vector<vector<int>> idx1, idx2; 
    bowExtractor->compute(im1, im1Keypoints, im1Descriptors, &idx1); 
    bowExtractor->compute(im2, im2Keypoints, im2Descriptors, &idx2); 
    // compare the distributions 
    Mat hist1 = Mat::zeros(vocabSize, 1, CV_32F); 
    Mat hist2 = Mat::zeros(vocabSize, 1, CV_32F); 

    for (int i = 0; i < vocabSize; i++) 
    { 
     hist1.at<float>(i) = (float)idx1[i].size(); 
     hist2.at<float>(i) = (float)idx2[i].size(); 
    } 
    normalize(hist1, hist1); 
    normalize(hist2, hist2); 
    double comp = compareHist(hist1, hist2, CV_COMP_BHATTACHARYYA); 

    cout << comp << endl; 
    // low BHATTACHARYYA distance means a good match of features in the two regions 
    if (comp < .2) 
    { 
     // mark this contour as a region having sparse motion 
     drawContours(disp, contours, j, Scalar(0, 255, 255), 2); 
    } 
    else 
    { 
     // mark this contour as object arrival/removal region 
     drawContours(disp, contours, j, Scalar(0, 0, 255), 2); 
    } 
} 
+0

Мне пришлось добавить if ((im1Keypoints.size() Maystro

 Смежные вопросы

  • Нет связанных вопросов^_^