Ideia geral
Opção 1: carregue as duas imagens como matrizes ( scipy.misc.imread
) e calcule uma diferença entre elementos (pixel por pixel). Calcule a norma da diferença.
Opção 2: Carregue as duas imagens. Calcule algum vetor de recurso para cada um deles (como um histograma). Calcule a distância entre vetores de recursos em vez de imagens.
No entanto, existem algumas decisões a serem tomadas primeiro.
Questões
Você deve responder a estas perguntas primeiro:
As imagens têm a mesma forma e dimensão?
Caso contrário, pode ser necessário redimensioná-las ou cortá-las. A biblioteca PIL ajudará a fazê-lo em Python.
Se forem tiradas com as mesmas configurações e o mesmo dispositivo, provavelmente são as mesmas.
As imagens estão bem alinhadas?
Caso contrário, convém executar a correlação cruzada primeiro, para encontrar o melhor alinhamento primeiro. O SciPy tem funções para fazer isso.
Se a câmera e a cena estiverem paradas, é provável que as imagens estejam bem alinhadas.
A exposição das imagens é sempre a mesma? (A luminosidade / contraste é o mesmo?)
Caso contrário, você pode normalizar as imagens.
Mas tenha cuidado, em algumas situações isso pode fazer mais mal do que bem. Por exemplo, um único pixel brilhante em um fundo escuro tornará a imagem normalizada muito diferente.
As informações de cores são importantes?
Se você quiser observar alterações de cores, terá um vetor de valores de cores por ponto, em vez de um valor escalar, como na imagem em escala de cinza. Você precisa de mais atenção ao escrever esse código.
Existem arestas distintas na imagem? É provável que eles se mexam?
Se sim, você pode aplicar o algoritmo de detecção de borda primeiro (por exemplo, calcular gradiente com as transformações Sobel ou Prewitt, aplicar algum limite) e comparar as arestas na primeira imagem com as arestas na segunda.
Há ruído na imagem?
Todos os sensores poluem a imagem com uma certa quantidade de ruído. Sensores de baixo custo têm mais ruído. Você pode aplicar alguma redução de ruído antes de comparar imagens. O desfoque é a abordagem mais simples (mas não a melhor) aqui.
Que tipo de mudanças você deseja observar?
Isso pode afetar a escolha da norma a ser usada para a diferença entre imagens.
Considere usar a norma Manhattan (a soma dos valores absolutos) ou a norma zero (o número de elementos que não é igual a zero) para medir quanto a imagem mudou. O primeiro dirá o quanto a imagem está desativada, o segundo dirá apenas quantos pixels diferem.
Exemplo
Presumo que suas imagens estejam bem alinhadas, com o mesmo tamanho e formato, possivelmente com exposição diferente. Para simplificar, eu os converto em escala de cinza, mesmo que sejam imagens coloridas (RGB).
Você precisará destas importações:
import sys
from scipy.misc import imread
from scipy.linalg import norm
from scipy import sum, average
Função principal, leia duas imagens, converta em escala de cinza, compare e imprima resultados:
def main():
file1, file2 = sys.argv[1:1+2]
# read images as 2D arrays (convert to grayscale for simplicity)
img1 = to_grayscale(imread(file1).astype(float))
img2 = to_grayscale(imread(file2).astype(float))
# compare
n_m, n_0 = compare_images(img1, img2)
print "Manhattan norm:", n_m, "/ per pixel:", n_m/img1.size
print "Zero norm:", n_0, "/ per pixel:", n_0*1.0/img1.size
Como comparar img1
e img2
são matrizes 2D SciPy aqui:
def compare_images(img1, img2):
# normalize to compensate for exposure difference, this may be unnecessary
# consider disabling it
img1 = normalize(img1)
img2 = normalize(img2)
# calculate the difference and its norms
diff = img1 - img2 # elementwise for scipy arrays
m_norm = sum(abs(diff)) # Manhattan norm
z_norm = norm(diff.ravel(), 0) # Zero norm
return (m_norm, z_norm)
Se o arquivo for uma imagem colorida, imread
retornará uma matriz 3D, canais RGB médios (o último eixo da matriz) para obter intensidade. Não é necessário fazer isso para imagens em escala de cinza (por exemplo .pgm
):
def to_grayscale(arr):
"If arr is a color image (3D array), convert it to grayscale (2D array)."
if len(arr.shape) == 3:
return average(arr, -1) # average over the last axis (color channels)
else:
return arr
A normalização é trivial, você pode optar por normalizar para [0,1] em vez de [0,255]. arr
é uma matriz SciPy aqui, portanto, todas as operações são baseadas em elementos:
def normalize(arr):
rng = arr.max()-arr.min()
amin = arr.min()
return (arr-amin)*255/rng
Execute a main
função:
if __name__ == "__main__":
main()
Agora você pode colocar tudo isso em um script e executar duas imagens. Se compararmos a imagem consigo mesma, não há diferença:
$ python compare.py one.jpg one.jpg
Manhattan norm: 0.0 / per pixel: 0.0
Zero norm: 0 / per pixel: 0.0
Se borrarmos a imagem e comparamos com o original, há alguma diferença:
$ python compare.py one.jpg one-blurred.jpg
Manhattan norm: 92605183.67 / per pixel: 13.4210411116
Zero norm: 6900000 / per pixel: 1.0
PS Script compare.py inteiro .
Atualização: técnicas relevantes
Como a pergunta é sobre uma sequência de vídeo, em que os quadros provavelmente são quase os mesmos, e você procura algo incomum, gostaria de mencionar algumas abordagens alternativas que podem ser relevantes:
- subtração e segmentação em segundo plano (para detectar objetos em primeiro plano)
- fluxo óptico escasso (para detectar movimento)
- comparando histogramas ou outras estatísticas em vez de imagens
Eu recomendo dar uma olhada no livro “Learning OpenCV”, capítulos 9 (partes e segmentação de imagens) e 10 (rastreamento e movimento). O primeiro ensina a usar o método de subtração Background, o último fornece algumas informações sobre os métodos de fluxo óptico. Todos os métodos são implementados na biblioteca OpenCV. Se você usa Python, sugiro usar o OpenCV ≥ 2.3 e seu cv2
módulo Python.
A versão mais simples da subtração em segundo plano:
- aprenda o valor médio μ e o desvio padrão σ para cada pixel do plano de fundo
- compare os valores atuais de pixel com o intervalo de (μ-2σ, μ + 2σ) ou (μ-σ, μ + σ)
Versões mais avançadas levam em consideração séries temporais para cada pixel e lidam com cenas não estáticas (como mover árvores ou grama).
A idéia do fluxo óptico é pegar dois ou mais quadros e atribuir o vetor de velocidade a cada pixel (fluxo óptico denso) ou a alguns deles (fluxo óptico esparso). Para estimar o fluxo óptico esparso, você pode usar o método Lucas-Kanade (também é implementado no OpenCV). Obviamente, se houver muito fluxo (média alta acima dos valores máximos do campo de velocidade), algo estará se movendo no quadro e as imagens subseqüentes serão mais diferentes.
Comparar histogramas pode ajudar a detectar mudanças repentinas entre quadros consecutivos. Essa abordagem foi usada em Courbon et al, 2010 :
Semelhança de quadros consecutivos. A distância entre dois quadros consecutivos é medida. Se estiver muito alto, significa que o segundo quadro está corrompido e, portanto, a imagem é eliminada. A distância Kullback-Leibler , ou entropia mútua, nos histogramas dos dois quadros:
em que p e q são os histogramas dos quadros é usado. O limite é fixo em 0,2.