opencv - significa - seo imagenes wordpress
CV-Extrae diferencias entre dos imágenes (4)
Actualmente estoy trabajando en un sistema de intrusión basado en video vigilancia. Para completar esta tarea, tomo una instantánea del fondo de mi escena (supongo que está totalmente limpio, sin personas ni objetos en movimiento). Luego, comparo el cuadro que obtengo de la cámara de video (estática) y busco las diferencias. Tengo que poder verificar cualquier diferencia, no solo la forma humana o lo que sea, por lo que no puedo extraer características específicas.
Por lo general, tengo:
Estoy usando OpenCV, así que para comparar básicamente hago:
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)));
Aquí está el resultado:
Como puede ver, el brazo está despojado (supongo que debido al conflicto de diferencia de color) y lamentablemente esto no es lo que quiero.
Pensé en agregar el uso de
cv::Canny()
para detectar los bordes y llenar la parte faltante del brazo, pero lamentablemente (una vez más), solo resuelve el problema en pocas situaciones, no en la mayoría de ellos.
¿Hay algún algoritmo o técnica que pueda usar para obtener un informe de diferencia preciso ?
PD: Perdón por las imágenes. Debido a mi nueva suscripción, no tengo suficiente reputación.
EDITAR Uso la imagen en escala de grises aquí, pero estoy abierto a cualquier solución.
Este es un conocido problema clásico de visión por computadora llamado sustracción de fondo . Hay muchos enfoques que se pueden usar para resolver este problema, la mayoría de ellos ya están implementados, por lo que creo que primero debe echar un vistazo a varios algoritmos existentes, aquí está la implementación de código abierto de la mayoría de ellos: https://github.com/andrewssobral/bgslibrary (personalmente encontré SUBSENSE dando los mejores resultados, pero es muy lento)
Otra técnica para obtener las diferencias exactas de píxeles entre dos imágenes es utilizar el Índice de similitud estructural (SSIM) que se introdujo por primera vez en la
Evaluación de calidad de imagen
en papel
: de la visibilidad del error a la similitud estructural
.
Este método se puede usar para determinar si dos imágenes son idénticas y / o muestran diferencias debido a pequeñas discrepancias de imagen.
SSIM ya está implementado en la
biblioteca
de imágenes
skimage.measure.compare_ssim()
para el procesamiento de imágenes como
skimage.measure.compare_ssim()
La función
compare_ssim()
devuelve una
score
y una imagen de diferencia,
diff
.
El
score
representa el índice de similitud estructural promedio entre las dos imágenes de entrada y puede caer entre el rango
[-1,1]
con valores más cercanos a uno que representan mayor similitud.
Pero dado que solo le interesa en qué difieren las dos imágenes, la imagen de
diff
es en lo que nos centraremos.
Específicamente, la imagen de
diff
contiene las diferencias de imagen reales con regiones más oscuras que tienen más disparidad.
Las áreas más grandes de disparidad están resaltadas en negro, mientras que las diferencias más pequeñas están en gris.
Usando estas dos imágenes de entrada
Obtenemos este resultado
Similitud de imagen: 0.9587009832317672
El puntaje SSIM después de comparar las dos imágenes muestra que son muy similares
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()
Un problema en su código es
cv::threshold
que solo usa imágenes de 1 canal.
Encontrar la "diferencia" de píxeles entre dos imágenes en escala de grises a menudo conduce a resultados poco intuitivos.
Como sus imágenes proporcionadas están un poco traducidas o la cámara no estaba estacionaria, manipulé su imagen de fondo para agregar algo de primer plano:
imagen de fondo:
imagen de primer plano:
código:
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;
}
}
dando este resultado:
con esta imagen de diferencia:
en general, es difícil calcular una segmentación completa de primer plano / fondo a partir de interpretaciones de diferencias en píxeles.
Probablemente tendrá que agregar cosas de postprocesamiento para obtener una segmentación real, desde la que comienza desde la máscara de primer plano. No estoy seguro si todavía hay soluciones universales estables.
Como mencionó berak, en la práctica no será suficiente usar una sola imagen de fondo, por lo que tendrá que calcular / administrar su imagen de fondo con el tiempo. Hay muchos documentos que cubren este tema y todavía no existe una solución universal estable.
Aquí hay algunas pruebas más.
cv::cvtColor(backgroundImage, HSVbackgroundImagebg, CV_BGR2HSV); cv::cvtColor(currentImage, HSV_currentImage, CV_BGR2HSV);
espacio de color
HSV
:
cv::cvtColor(backgroundImage, HSVbackgroundImagebg, CV_BGR2HSV); cv::cvtColor(currentImage, HSV_currentImage, CV_BGR2HSV);
cv::cvtColor(backgroundImage, HSVbackgroundImagebg, CV_BGR2HSV); cv::cvtColor(currentImage, HSV_currentImage, CV_BGR2HSV);
y realizó las mismas operaciones en este espacio, lo que lleva a este resultado:
después de agregar algo de ruido a la entrada:
Me sale este resultado:
entonces quizás el umbral es un poco demasiado alto. Todavía le animo a que también eche un vistazo al espacio de color HSV, pero es posible que deba reinterpretar la "imagen de diferencia" y volver a escalar cada canal para combinar sus valores de diferencia.
Yo uso Python, este es mi resultado:
El código:
# 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)
Actualización, aquí está el código 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;
}
Respuestas similares: