Pergunta

Aqui está o que eu gostaria de fazer:

Estou tirando fotos com uma webcam em intervalos regulares. Mais ou menos como uma coisa lapso de tempo. No entanto, se nada realmente mudou, isto é, a imagem muito bonito aparência o mesmo, eu não quero para armazenar o instantâneo mais recente.

Eu imagino que há alguma maneira de quantificar a diferença, e eu teria que determinar empiricamente um limiar.

Eu estou procurando a simplicidade em vez de perfeição. Eu estou usando python.

Foi útil?

Solução

idéia Geral

Opção 1: Coloque as duas imagens como matrizes (scipy.misc.imread) e calcular uma (pixel a pixel) diferença elemento-wise. Calcule a norma da diferença.

Opção 2: Coloque as duas imagens. Calcule algum vetor de características para cada um deles (como um histograma). distância Calcular entre vetores de características, em vez de imagens.

No entanto, há algumas decisões a tomar em primeiro lugar.

Perguntas

Você deve responder a estas perguntas primeiro:

  • Tem imagens da mesma forma e dimensão?

    Se não, você pode precisar de redimensionar ou cortar-los. biblioteca PIL vai ajudar a fazê-lo em Python.

    Se eles são tomados com as mesmas configurações e o mesmo dispositivo, eles são provavelmente o mesmo.

  • são imagens bem alinhados?

    Se não, você pode querer executar correlação cruzada em primeiro lugar, para encontrar o melhor alinhamento em primeiro lugar. SciPy tem funções de fazê-lo.

    Se a câmera ea cena ainda são, as imagens são susceptíveis de ser bem alinhados.

  • é a exposição das imagens sempre o mesmo? (É claro / contrastar o mesmo?)

    Se não, você pode querer para normalizar imagens.

    Mas cuidado, em algumas situações isso pode fazer mais mal do que bem. Por exemplo, um único pixel brilhante sobre um fundo escuro vai fazer a imagem normalizada muito diferente.

  • É informação de cor importante?

    Se você quer mudanças aviso de cor, você terá um vector de valores de cor por ponto, ao invés de um valor escalar como na imagem em escala de cinza. Você precisa de mais atenção ao escrever tal código.

  • Existem arestas distintas na imagem? Eles estão propensos a mudar?

    Se sim, é possível aplicar o algoritmo de detecção de borda primeiro (por exemplo, calcular com gradiente de Sobel ou Prewitt transformar, aplicar algum limiar), e depois comparar bordas sobre a primeira imagem para bordas sobre a segunda.

  • Há ruído na imagem?

    Todos os sensores poluir a imagem com uma certa quantidade de ruído. sensores de baixo custo tem mais ruído. Você pode querer aplicar alguma redução de ruído antes de comparar imagens. Blur é o mais simples (mas não o melhor) abordagem aqui.

  • Que tipo de mudanças que você quer aviso?

    Isso pode afetar a escolha da norma a utilização para a diferença entre as imagens.

    Considere o uso de Manhattan norma (a soma dos valores absolutos) ou nula norma (o número de elementos não é igual a zero) para medir o quanto a imagem mudou. O primeiro vai te dizer o quanto a imagem estiver desligado, este último irá contam apenas quantos pixels diferentes.

Exemplo

Eu assumo as imagens estão bem alinhados, o mesmo tamanho e forma, possivelmente com exposição diferente. Para simplificar, eu convertê-los em tons de cinza, mesmo que sejam imagens coloridas (RGB).

Você vai precisar dessas importações:

import sys

from scipy.misc import imread
from scipy.linalg import norm
from scipy import sum, average

A função principal, leia duas imagens, converter para tons de cinza, comparar e imprimir os 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 arrays 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 é uma imagem colorida, imread retorna uma matriz 3D, canais média RGB (o último eixo matriz) para obter intensidade. Não há necessidade de fazê-lo para imagens em tons 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 escolher para normalizar a [0,1] em vez de [0255]. arr é uma matriz SciPy aqui, então todas as operações são elemento-wise:

def normalize(arr):
    rng = arr.max()-arr.min()
    amin = arr.min()
    return (arr-amin)*255/rng

Execute a função main:

if __name__ == "__main__":
    main()

Agora você pode colocar tudo isso em um script e executar contra duas imagens. Se compararmos imagem para si mesmo, 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 borrar a imagem e comparar 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

P.S. Todo o compare.py script.

Update: técnicas relevantes

Como a pergunta é sobre uma seqüência de vídeo, onde os quadros são susceptíveis de ser quase o mesmo, e você olha para algo incomum, eu gostaria de mencionar algumas abordagens alternativas que podem ser relevantes:

  • subtração de fundo e segmentação (para detectar objetos em primeiro plano)
  • fluxo óptico escassa (para detectar movimento)
  • comparar histogramas ou algumas outras estatísticas em vez de imagens

Eu fortemente recomendo dar uma olhada “Aprendizagem OpenCV” livro, capítulos 9 (partes de imagem e segmentação) e 10 (Rastreamento e movimento). O ex ensina a utilizar o método de subtracção do fundo, o último dá algumas informações sobre os métodos de fluxo óptico. Todos os métodos são implementados na biblioteca OpenCV. Se você usar Python, eu sugiro usar OpenCV = 2,3, e seu módulo Python cv2.

A versão mais simples da subtração de fundo:

  • aprender a µ valor médio e s desvio padrão para cada pixel do fundo
  • comparar valores de pixel corrente ao intervalo de (µ-2s, µ + 2s) ou (µ-s, µ + s)

Versões mais avançadas fazem ter em conta séries temporais para cada pixel e punho non-static cenas (como mover árvores ou grama).

A idéia de fluxo óptico é levar dois ou mais quadros e vetor atribuir velocidade para cada pixel (fluxo óptico denso) ou para alguns deles (fluxo óptico escassa). Para estimar o fluxo óptico escassa, você pode usar método Lucas-Kanade (-lo também é implementado no OpenCV). Obviamente, se houver uma grande quantidade de fluxo (alta média sobre os valores máximo do campo de velocidade), então algo está se movendo no quadro, e as imagens subseqüentes são mais diferentes.

Comparando histogramas pode ajudar a detectar mudanças bruscas entre quadros consecutivos. Esta abordagem foi utilizada em Courbon et al, 2010 :

Semelhança entre quadros consecutivos. A distância entre dois quadros consecutivos é medido. Se for demasiado alta, isto significa que o segundo quadro está danificado e, portanto, a imagem é eliminado. O Kullback-Leibler distância, ou entropia mútuo, nos histogramas da dois quadros:

$$ d (p, q) = \ sum_i p (i) \ log (p (i) / q ( i)) $$

que p e q são os histogramas dos quadros é usado. O limite está fixado em 0,2.

Outras dicas

Uma solução simples:

Encode a imagem como um jpeg e olhar para uma mudança substancial na tamanho do arquivo .

Eu tenho implementado algo semelhante com miniaturas de vídeo, e teve muito sucesso e escalabilidade.

Você pode comparar duas imagens usando as funções de PIL .

import Image
import ImageChops

im1 = Image.open("splash.png")
im2 = Image.open("splash2.png")

diff = ImageChops.difference(im2, im1)

O objeto diff é uma imagem na qual cada pixel é o resultado da subtração dos valores de cor desse pixel na segunda imagem a partir da primeira imagem. Usando a imagem diff você pode fazer várias coisas. O mais simples é a função diff.getbbox(). Vai dizer-lhe o retângulo mínimo que contém todas as alterações entre as suas duas imagens.

Você provavelmente pode implementar aproximações das outras coisas mencionadas aqui, usando funções de PIL também.

Dois popular e métodos relativamente simples são: (a) a distância euclidiana já foi sugerido, ou (b) normalizou correlação cruzada. Normalizada de correlação cruzada tende a ser visivelmente mais robusto para acender alterações do que a correlação cruzada simples. Wikipedia dá uma fórmula para o normalizada correlação cruzada . métodos mais sofisticados existe também, mas eles exigem um pouco mais de trabalho.

Usando numpy-como sintaxe,

dist_euclidean = sqrt(sum((i1 - i2)^2)) / i1.size

dist_manhattan = sum(abs(i1 - i2)) / i1.size

dist_ncc = sum( (i1 - mean(i1)) * (i2 - mean(i2)) ) / (
  (i1.size - 1) * stdev(i1) * stdev(i2) )

assumindo que i1 e i2 são 2D matrizes de imagem em tons de cinza.

Uma coisa trivial para tentar:

Reamostrar tanto imagens para miniaturas pequenas (por exemplo 64 x 64) e comparar as miniaturas de pixel-por-pixel com um determinado limiar. Se as imagens originais são quase os mesmos, as miniaturas resolução redefinida será muito semelhante ou mesmo exatamente o mesmo. Este método se encarrega de ruído que pode ocorrer especialmente em cenas de pouca luz. Pode até ser melhor se você ir em tons de cinza.

Estou abordando especificamente a questão de como calcular se eles são "bastante diferente". Eu suponho que você pode descobrir como subtrair os pixels um por um.

Em primeiro lugar, gostaria de tomar um monte de imagens com nada mudando, e descobrir a quantidade máxima que qualquer pixel muda apenas por causa de variações na captura, o ruído no sistema de imagem, artefatos de compressão JPEG e mudanças momento a momento em iluminação. Talvez você verá que 1 ou 2 diferenças bit são esperados, mesmo quando nada se move.

Em seguida, para o teste "real", você quer um critério assim:

  • mesmo se até P pixels diferem por não mais do que E.

Assim, talvez, se E = 0,02, P = 1,000, o que significaria (aproximadamente) que seria "diferente" se quaisquer alterações de pixel individuais por mais de ~ 5 unidades (assumindo imagens de 8 bits), ou se mais de 1000 pixels teve qualquer erro em tudo.

Este destina-se principalmente como uma boa técnica "triagem" para identificar rapidamente imagens que estão perto o suficiente para não precisar de um exame mais aprofundado. As imagens que "falha" pode então mais para um mais elaborado técnica / dispendioso que não teria falsos positivos se a câmera balançou bit, por exemplo, ou foi mais robusta para iluminação alterações.

Eu corro um projeto open source, OpenImageIO , que contém um utilitário chamado "idiff" que compara diferenças com limiares assim (ainda mais elaborado, na verdade). Mesmo se você não quiser usar este software, você pode querer olhar para a fonte para ver como nós o fizemos. É usado comercialmente um pouco e esta técnica de limiar foi desenvolvido para que pudéssemos ter um conjunto de testes para processamento e software de processamento de imagem, com "imagens de referência" que podem ter pequenas diferenças de plataforma-to-plataforma ou como fizemos pequenos ajustes para algoritmos tha, portanto, queria um "jogo dentro da tolerância" operação.

A maioria das respostas dadas não vai lidar com níveis de iluminação.

Eu faria primeiro normalize a imagem para um nível de luz padrão antes de fazer a comparação.

Outra agradável, maneira simples para medir a similaridade entre duas imagens:

import sys
from skimage.measure import compare_ssim
from skimage.transform import resize
from scipy.ndimage import imread

# get two images - resize both to 1024 x 1024
img_a = resize(imread(sys.argv[1]), (2**10, 2**10))
img_b = resize(imread(sys.argv[2]), (2**10, 2**10))

# score: {-1:1} measure of the structural similarity between the images
score, diff = compare_ssim(img_a, img_b, full=True)
print(score)

Se outros estão interessados ??em uma forma mais poderosa para comparar similaridade imagem, eu coloquei uma tutorial e web aplicativo para medir e visualizar imagens semelhantes usando Tensorflow.

Eu tive um problema semelhante no trabalho, eu estava reescrevendo nossa imagem transformar endpoint e eu queria verificar que a nova versão estava produzindo a mesma ou quase a mesma saída que a versão antiga. Então eu escrevi o seguinte:

https://github.com/nicolashahn/diffimg

que opera em imagens do mesmo tamanho, e a um nível por pixel, mede a diferença de valores em cada canal: R, G, B (, A), leva a diferença média desses canais, e, em seguida, médias a diferença sobre todos os pixels, e retorna uma relação.

Por exemplo, com uma imagem de 10x10 pixels de brancos, ea mesma imagem, mas um pixel mudou para vermelho, a diferença naquele pixel é 1/3 ou 0,33 ... (RGB 0,0,0 vs 255, 0,0) e em todos os outros pixels é 0. Com 100 pixels no total, 0,33 ... / 100 = a ~ 0,33% diferença na imagem.

Eu acredito que este iria funcionar perfeitamente para o projeto de OP (Sei que este é um post muito velho agora, mas postagem para futuras StackOverflowers que também querem comparar imagens em python).

Você já viu o algoritmo para encontrar imagens semelhantes pergunta? Check it out para ver sugestões.

Gostaria de sugerir uma transformação wavelet de seus quadros (eu escrevi uma extensão C para que o uso de transformação Haar); em seguida, comparando os índices das maiores (proporcionalmente) factores wavelet entre as duas imagens, você deve obter uma aproximação similaridade numérica.

Peço desculpas se este é tarde demais para responder, mas desde que eu tenho feito algo que eu semelhante pensei que poderia contribuir de alguma forma.

Talvez com OpenCV você poderia usar a correspondência de modelo. Supondo que você estiver usando uma webcam como você disse:

  1. Simplifique as imagens (thresholding talvez?)
  2. Aplicar casamento de modelos e verificar o max_val com minMaxLoc

Dica: max_val (ou min_val dependendo do método utilizado) vai lhe dar números, grandes números. Para obter a diferença em porcentagem, modelo usar a correspondência com a mesma imagem -. O resultado será o seu 100%

código Pseudo para exemplificar:

previous_screenshot = ...
current_screenshot = ...

# simplify both images somehow

# get the 100% corresponding value
res = matchTemplate(previous_screenshot, previous_screenshot, TM_CCOEFF)
_, hundred_p_val, _, _ = minMaxLoc(res)

# hundred_p_val is now the 100%

res = matchTemplate(previous_screenshot, current_screenshot, TM_CCOEFF)
_, max_val, _, _ = minMaxLoc(res)

difference_percentage = max_val / hundred_p_val

# the tolerance is now up to you

Hope isso ajuda.

Earth movers distância pode ser exatamente o que você precisa. Pode ser ABIT pesado para implementar em tempo real, no entanto.

E sobre o cálculo do Manhattan Distância das duas imagens. Isso dá-lhe n * n valores. Em seguida, você poderia fazer algo parecido com uma linha média para reduzir a valores n e uma função mais que para obter um único valor.

Eu tenho tido muita sorte com imagens jpg tiradas com a mesma câmera em um tripé por (1) simplificando enormemente (como indo de 3000 pixels de largura de 100 pixels de largura ou ainda menos) (2) achatamento cada matriz jpg num único vector de (3) aos pares correlacionando imagens sequenciais com um algoritmo de correlação simples para obter o coeficiente de correlação (4) em quadratura com o coeficiente de correlação para obter r-quadrado (isto é fracção de variabilidade em uma imagem explicado pela variação no seguinte) (5), geralmente no meu aplicativo se r-quadrado <0,9, digo as duas imagens são diferentes e algo aconteceu entre eles.

Esta é robusto e rápido na minha aplicação (Mathematica 7)

Vale a pena brincar com a parte da imagem que você está interessado e focando que cortando todas as imagens para aquela pequena área, caso contrário, uma mudança distante-de-the-câmera, mas importante vai ser desperdiçada.

Eu não sei como usar Python, mas tenho certeza que ele faz correlações, também, não?

Você pode calcular o histograma de ambas as imagens e, em seguida, calcular o Bhattacharyya Coeficiente , este é um algoritmo muito rápido e eu tê-lo usado para detectar alterações tiro em um vídeo de críquete (em C usando openCV)

Confira como Transformada de Haar são implementadas por isk-daemon . Você poderia usá-lo de imgdb C ++ código para calcular a diferença entre as imagens on-the-fly:

isk-daemon é um servidor de banco de dados de código aberto capaz de agregar baseada em conteúdo (visual) pesquisa de imagens para qualquer site relacionado imagem ou software.

Esta tecnologia permite que usuários de qualquer website imagem relacionada ou software a esboçar em um widget que imagem eles querem encontrar e ter o website resposta a eles as imagens mais semelhantes ou simplesmente pedir para fotos mais similares em cada página de detalhe da imagem.

Eu tive o mesmo problema e escreveu um módulo python simples que compara duas imagens do mesmo tamanho usando ImageChops do travesseiro para criar uma imagem preto / branco diff e resume os valores do histograma.

Você pode obter tanto esta pontuação diretamente, ou um valor percentual em relação a um diff branco preto vs. completo.

Ele também contém uma função is_equal simples, com a possibilidade de fornecer uma difusa-limiar abaixo (e incluindo) a imagem passa como iguais.

A abordagem não é muito elaborado, mas talvez é de uso para outra lá fora lutando com o mesmo problema.

https://pypi.python.org/pypi/imgcompare/

Uma abordagem um pouco mais íntegro é usar um descritor global para comparar imagens, tais como GIST ou centristas. Uma função hash, tal como descrito aqui , igualmente oferece uma solução similar.

import os
from PIL import Image
from PIL import ImageFile
import imagehash

#just use to the size diferent picture
def compare_image(img_file1, img_file2):
    if img_file1 == img_file2:
        return True
    fp1 = open(img_file1, 'rb')
    fp2 = open(img_file2, 'rb')

    img1 = Image.open(fp1)
    img2 = Image.open(fp2)

    ImageFile.LOAD_TRUNCATED_IMAGES = True
    b = img1 == img2

    fp1.close()
    fp2.close()

    return b





#through picturu hash to compare
def get_hash_dict(dir):
    hash_dict = {}
    image_quantity = 0
    for _, _, files in os.walk(dir):
        for i, fileName in enumerate(files):
            with open(dir + fileName, 'rb') as fp:
                hash_dict[dir + fileName] = imagehash.average_hash(Image.open(fp))
                image_quantity += 1

    return hash_dict, image_quantity

def compare_image_with_hash(image_file_name_1, image_file_name_2, max_dif=0):
    """
    max_dif: The maximum hash difference is allowed, the smaller and more accurate, the minimum is 0.
    recommend to use
    """
    ImageFile.LOAD_TRUNCATED_IMAGES = True
    hash_1 = None
    hash_2 = None
    with open(image_file_name_1, 'rb') as fp:
        hash_1 = imagehash.average_hash(Image.open(fp))
    with open(image_file_name_2, 'rb') as fp:
        hash_2 = imagehash.average_hash(Image.open(fp))
    dif = hash_1 - hash_2
    if dif < 0:
        dif = -dif
    if dif <= max_dif:
        return True
    else:
        return False


def compare_image_dir_with_hash(dir_1, dir_2, max_dif=0):
    """
    max_dif: The maximum hash difference is allowed, the smaller and more accurate, the minimum is 0.

    """
    ImageFile.LOAD_TRUNCATED_IMAGES = True
    hash_dict_1, image_quantity_1 = get_hash_dict(dir_1)
    hash_dict_2, image_quantity_2 = get_hash_dict(dir_2)

    if image_quantity_1 > image_quantity_2:
        tmp = image_quantity_1
        image_quantity_1 = image_quantity_2
        image_quantity_2 = tmp

        tmp = hash_dict_1
        hash_dict_1 = hash_dict_2
        hash_dict_2 = tmp

    result_dict = {}

    for k in hash_dict_1.keys():
        result_dict[k] = None

    for dif_i in range(0, max_dif + 1):
        have_none = False

        for k_1 in result_dict.keys():
            if result_dict.get(k_1) is None:
                have_none = True

        if not have_none:
            return result_dict

        for k_1, v_1 in hash_dict_1.items():
            for k_2, v_2 in hash_dict_2.items():
                sub = (v_1 - v_2)
                if sub < 0:
                    sub = -sub
                if sub == dif_i and result_dict.get(k_1) is None:
                    result_dict[k_1] = k_2
                    break
    return result_dict


def main():
    print(compare_image('image1\\815.jpg', 'image2\\5.jpg'))
    print(compare_image_with_hash('image1\\815.jpg', 'image2\\5.jpg', 6))
    r = compare_image_dir_with_hash('image1\\', image2\\', 10)
    for k in r.keys():
        print(k, r.get(k))


if __name__ == '__main__':
    main()
  • saída:

    Falso
    verdadeira
    image2 \ 5.jpg image1 \ 815.jpg
    image2 \ 6.jpg image1 \ 819.jpg
    image2 \ 7.jpg image1 \ 900.jpg
    image2 \ 8.jpg image1 \ 998.jpg
    image2 \ 9.jpg image1 \ 1012.jpg

  • O exemplo as imagens:

    • 815.jpg
      815.jpg

    • 5.jpg
      5.jpg

Eu acho que você poderia simplesmente calcular a distância euclidiana (ou seja, sqrt (soma dos quadrados das diferenças, pixel por pixel)) entre a luminância das duas imagens, e considerá-los iguais se isso cai sob algum limiar empírico. E é melhor você iria fazê-lo envolvendo uma função C.

Existem muitas métricas lá fora, para avaliar se duas imagens parecem com / o quanto eles se parecem.

Eu não vou entrar em qualquer código aqui, porque eu acho que deveria ser um problema científico, à excepção de um problema técnico.

Geralmente, a questão está relacionada com a percepção do ser humano em imagens, de modo que cada algoritmo tem o seu apoio em traços sistema visual humano.

abordagens clássicas são:

Visible diferenças preditor: um algoritmo para a avaliação da fidelidade de imagem ( https://www.spiedigitallibrary.org/conference-proceedings-of-spie/1666/0000 /Visible-differences-predictor--an-algorithm-for-the-assessment-of/10.1117/12.135952.short?SSO=1 )

Imagem de Avaliação da Qualidade: De Erro Visibilidade para estrutural Similaridade ( http: //www.cns.nyu.edu/pub/lcv/wang03-reprint.pdf )

FSIM: Um Recurso Índice de Similaridade para a imagem Avaliação da Qualidade ( https : //www4.comp.polyu.edu.hk/~cslzhang/IQA/TIP_IQA_FSIM.pdf )

Entre eles, SSIM (Avaliação da Qualidade Image: De Erro Visibilidade para estrutural Similaridade) é o mais fácil de calcular e sua sobrecarga é também pequena, como relatado em outro artigo "Qualidade de Imagem avaliação baseada no inclinação Semelhança" ( https://www.semanticscholar.org/paper/ imagem-Qualidade-avaliação baseada-on-inclinação-Liu-Lin / 2b819bef80c02d5d4cb56f27b202535e119df988 ).

Existem muitas mais outras abordagens. Dê uma olhada no Google Scholar e procurar algo como "diferença visual", "avaliação de qualidade de imagem", etc, se você estiver interessado / realmente se preocupam com o art.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top