Cálculo eficiente da média de vizinhança adaptada a limites

Eu tenho uma imagem com valores que variam de 0 a 1. O que eu gosto de fazer é a média simples.
Mas, mais especificamente, para uma célula na borda da imagem, eu gostaria de calcular a média dos pixels para aquela parte da vizinhança / kernel que está dentro da extensão da imagem. Na verdade, isso se resume a adaptar o denominador da "fórmula média", o número de pixels pelos quais você divide a soma.

Eu consegui fazer isso como mostrado abaixo comscipy.ndimage.generic_filter, mas isso está longe de ser eficiente em termos de tempo.

def fnc(buffer, count):
    n = float(sum(buffer < 2.0))
    sum = sum(buffer) - ((count - b) * 2.0)
    return (sum / n)

avg = scipy.ndimage.generic_filter(image, fnc, footprint = kernel, \
                                   mode = 'constant', cval = 2.0,   \
                                   extra_keywords = {'count': countkernel})

Detalhes

kernel = matriz quadrada (círculo representado por uns)Preenchimento com 2 e não por zeros desde então eu não pude separar adequadamente zeros da área acolchoada e zeros da varredura realcountkernel = número de pessoas nokerneln = número de células que estão dentroimage excluindo as células da área acolchoada identificadas por valores de 2Corrigir osum subtraindo (número de celas acolchoadas * 2.0) da soma total de bairro original

Atualização (s)

1) Preenchimento com NaNs aumenta o cálculo com cerca de 30%:

    def fnc(buffer):
        return (numpy.nansum(buffer) / numpy.sum([~numpy.isnan(buffer)]))

    avg = scipy.ndimage.generic_filter(image, fnc, footprint = kernel, \
                                       mode = 'constant', cval = float(numpy.nan)

2) Aplicando a solução proposta porYves Daoust (resposta aceita), reduz definitivamente o tempo de processamento a um mínimo:

    def fnc(buffer):
        return numpy.sum(buffer)

    sumbigimage = scipy.ndimage.generic_filter(image, fnc, \
                                               footprint = kernel, \
                                               mode = 'constant', \
                                               cval = 0.0)
    summask     = scipy.ndimage.generic_filter(mask, fnc, \
                                               footprint = kernel, \
                                               mode = 'constant', \
                                               cval = 0.0)
    avg = sumbigimage / summask

3) Com base emYves ' dica para usar uma imagem binária adicional, que, de fato, está aplicando uma máscara, me deparei com o princípio damatrizes mascaradas. Como tal, apenas uma matriz deve ser processada porque uma matriz mascarada "mescla" a imagem e mascara os arrays juntos.
Um pequeno detalhe sobre a matriz de máscara: em vez de preencher a parte interna (extensão da imagem original) com 1s e preencher a parte externa (borda) com 0s como feito na atualização anterior, você deve fazer o contrário. Um 1 em um array mascarado significa 'inválido', um 0 significa 'válido'.
Este código é até 50% mais rápido que o código fornecido na atualização 2):

    maskedimg = numpy.ma.masked_array(imgarray, mask = maskarray)

    def fnc(buffer):
        return numpy.mean(buffer)

    avg = scipy.ndimage.generic_filter(maskedimg, fnc, footprint = kernel, \
                                       mode = 'constant', cval = 0.0)

-> Eu devo me corrigir aqui!
Eu devo estar enganado durante a validação, pois depois de algumas corridas de cálculo, parecia quescipy.ndimage.<filters> Não é possível manipular masked_arrays nesse sentido que durante a operação de filtro a máscara não é levada em conta.
Algumas outras pessoas mencionaram isso também, comoAqui eAqui.

O poder de uma imagem ...

cinza: extensão da imagem a ser processadabranco: área acolchoada (no meu caso preenchida com 2.0's)tons vermelhos: extensão do kernelvermelho escuro: neighbourhoud eficazvermelho claro: parte do bairro a ser ignorado

Como essa parte do código pragmática pode ser alterada para melhorar o desempenho do cálculo?

Muito obrigado antecipadamente!

questionAnswers(2)

yourAnswerToTheQuestion