CV - Извлечение различий между двумя изображениями

В настоящее время я работаю над системой вторжения, основанной на видеонаблюдении. Чтобы выполнить эту задачу, я делаю снимок фона моей сцены (предположим, что он полностью чист, нет людей или движущихся объектов). Затем я сравниваю рамку, которую я получаю от (статической) видеокамеры, и ища различия. Я должен быть в состоянии проверить любые различия, а не только человеческую форму или что-то еще, поэтому я не могу выделить конкретную функцию.

Как правило, у меня есть:

http://postimg.org/image/dxtcp4u8h/ p >

Я использую OpenCV, поэтому для сравнения я в основном делаю:

cv::Mat bg_frame;
cv::Mat cam_frame;
cv::Mat motion;

cv::absdiff(bg_frame, cam_frame, motion);
cv::threshold(motion, motion, 80, 255, cv::THRESH_BINARY);
cv::erode(motion, motion, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3,3)));

Вот результат:

http://postimg.org/image/3kz0o62id/ p >

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

Я подумал о том, чтобы добавить использование cv::Canny(), чтобы обнаружить края и заполнить недостающую часть руки, но, к сожалению (еще раз), она решает проблему только в немногих ситуациях, а не в большинстве из них.

Есть ли какой-либо алгоритм или метод, которые я мог бы использовать для получения точного отчета о различиях?

PS: Извините за изображения. Из-за моей новой подписки у меня недостаточно репутации.

ИЗМЕНИТЬ Здесь я использую изображение в оттенках серого, но я открыт для любого решения.

Ответы

Ответ 1

Одна проблема в вашем коде cv::threshold, которая использует только одноканальные изображения. Поиск "разности" в пикселях между двумя изображениями только в оттенках серого часто приводит к неинтуитивным результатам.

Так как ваши предоставленные изображения немного переведены или камера не была неподвижной, я манипулировал вашим фоновым изображением, чтобы добавить передний план:

фоновое изображение:

enter image description here

изображение переднего плана:

enter image description here

код:

    cv::Mat diffImage;
    cv::absdiff(backgroundImage, currentImage, diffImage);

    cv::Mat foregroundMask = cv::Mat::zeros(diffImage.rows, diffImage.cols, CV_8UC1);

    float threshold = 30.0f;
    float dist;

    for(int j=0; j<diffImage.rows; ++j)
        for(int i=0; i<diffImage.cols; ++i)
        {
            cv::Vec3b pix = diffImage.at<cv::Vec3b>(j,i);

            dist = (pix[0]*pix[0] + pix[1]*pix[1] + pix[2]*pix[2]);
            dist = sqrt(dist);

            if(dist>threshold)
            {
                foregroundMask.at<unsigned char>(j,i) = 255;
            }
        }

давая этот результат:

enter image description here

с этим разностным изображением:

enter image description here

в целом сложно вычислить полную сегментацию переднего плана/фона из интерпретаций разности по пикселям.

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

Как упоминалось в berak, на практике этого недостаточно, чтобы использовать одно фоновое изображение, поэтому вам придется вычислять/управлять фоновым изображением с течением времени. Есть много работ, посвященных этой теме, и afaik нет стабильного универсального решения.

вот еще несколько тестов. Я преобразовал в цветовое пространство HSV: cv::cvtColor(backgroundImage, HSVbackgroundImagebg, CV_BGR2HSV); cv::cvtColor(currentImage, HSV_currentImage, CV_BGR2HSV); и выполнил те же операции в этом пространстве, что привело к этому результату:

enter image description here

после добавления некоторого шума на вход:

enter image description here

Получаю этот результат:

enter image description here

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

Ответ 2

Я использую Python, это мой результат:

enter image description here

Код:

# 2017.12.22 15:48:03 CST
# 2017.12.22 16:00:14 CST
import cv2
import numpy as np

img1 = cv2.imread("img1.png")
img2 = cv2.imread("img2.png")
diff = cv2.absdiff(img1, img2))
mask = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)

th = 1
imask =  mask>th

canvas = np.zeros_like(img2, np.uint8)
canvas[imask] = img2[imask]

cv2.imwrite("result.png", canvas)

Обновление, вот код C++:

//! 2017.12.22 17:05:18 CST
//! 2017.12.22 17:22:32 CST

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main() {

    Mat img1 = imread("img3_1.png");
    Mat img2 = imread("img3_2.png");

    // calc the difference
    Mat diff;
    absdiff(img1, img2, diff);

    // Get the mask if difference greater than th
    int th = 10;  // 0
    Mat mask(img1.size(), CV_8UC1);
    for(int j=0; j<diff.rows; ++j) {
        for(int i=0; i<diff.cols; ++i){
            cv::Vec3b pix = diff.at<cv::Vec3b>(j,i);
            int val = (pix[0] + pix[1] + pix[2]);
            if(val>th){
                mask.at<unsigned char>(j,i) = 255;
            }
        }
    }

    // get the foreground
    Mat res;
    bitwise_and(img2, img2, res, mask);

    // display
    imshow("res", res);
    waitKey();
    return 0;
}

Подобные ответы:

  1. CV - Извлечь различия между двумя изображениями

  2. При поиске разницы между двумя картинками разница в OpenCV больше, чем должна быть

Ответ 3

Это хорошо известная классическая проблема компьютерного зрения, называемая вычитанием фона. Есть много подходов, которые могут быть использованы для решения этой проблемы, большинство из них уже реализованы, поэтому я думаю, что вы должны сначала взглянуть на несколько существующих алгоритмов, вот реализация большинства из них с открытым исходным кодом: https://github.com/andrewssobral/bgslibrary (лично я обнаружил, что SUBSENSE дает лучшие результаты, но он смертельно медленный)

Ответ 4

Другой метод получения точных различий в пикселях между двумя изображениями состоит в использовании индекса структурного сходства (SSIM), впервые представленного в статье Оценка качества изображения: от видимости ошибок до структурного сходства. Этот метод может использоваться, чтобы определить, являются ли два изображения идентичными и/или демонстрировать различия из-за крошечных расхождений в изображениях. SSIM уже реализован в библиотеке изображений scikit для обработки изображений как skimage.measure.compare_ssim()

Функция compare_ssim() возвращает score и разностное изображение, diff. score представляет средний индекс структурного сходства между двумя входными изображениями и может находиться в диапазоне [-1,1] со значениями, близкими к единице, представляющей более высокое сходство. Но поскольку вас интересует только то, где эти два изображения различаются, мы сосредоточимся на изображении diff. В частности, изображение diff содержит фактические различия изображения с более темными областями, имеющими большее несоответствие. Большие области несоответствия выделены черным, а меньшие различия - серым.


Использование этих двух входных изображений

Мы получаем этот результат

Image similarity: 0.9587009832317672

Оценка SSIM после сравнения двух изображений показывает, что они очень похожи

from skimage.measure import compare_ssim
import cv2

image1 = cv2.imread('1.png')
image2 = cv2.imread('2.png')

# Convert images to grayscale
image1_gray = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
image2_gray = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)

# Compute SSIM between two images
(score, diff) = compare_ssim(image1_gray, image2_gray, full=True)
print("Image similarity:", score)

# The diff image contains the actual image differences between the two images
# and is represented as a floating point data type in the range [0,1] 
# so we must convert the array to 8-bit unsigned integers in the range
# [0,255] image1 we can use it with OpenCV
diff = (diff * 255).astype("uint8")

cv2.imshow('diff', diff)
cv2.waitKey()