Effiziente Berechnung des grenzenangepassten Nachbarschaftsdurchschnitts

Ich habe ein Bild mit Werten zwischen 0 und 1. Was ich gerne mache, ist eine einfache Mittelung.
Genauer gesagt, für eine Zelle am Rand des Bildes möchte ich den Durchschnitt der Pixel für den Teil der Nachbarschaft / des Kernels berechnen, der innerhalb der Ausdehnung des Bildes liegt. Tatsächlich läuft dies darauf hinaus, den Nenner der 'Mittelwertformel' anzupassen, die Anzahl der Pixel, durch die Sie die Summe dividieren.

Ich habe das wie unten gezeigt mit geschafftscipy.ndimage.generic_filterDies ist jedoch alles andere als zeiteffizient.

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})

Einzelheiten

kernel = quadratisches Array (Kreis durch Einsen dargestellt)Auffüllen mit 2en und nicht durch Nullen seitdem konnte ich Nullen des aufgefüllten Bereichs und Nullen des tatsächlichen Rasters nicht richtig trennencountkernel = Anzahl der Einsen in derkerneln = Anzahl der Zellen, die innerhalb liegenimage durch Ausschließen der Zellen des gepolsterten Bereichs, der durch Werte von 2 identifiziert istKorrigiere dassum durch Subtrahieren (Anzahl der aufgefüllten Zellen * 2,0) von der Gesamtsumme der ursprünglichen Nachbarschaft

Aktualisierung)

1) Auffüllen mit NaNs erhöht die Berechnung um ca. 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) Anwendung der von vorgeschlagenen LösungYves Daoust (akzeptierte Antwort), reduziert definitiv die Bearbeitungszeit auf ein Minimum:

    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) Aufbauend aufYves tipp um ein zusätzliches binäres bild zu verwenden, das in der tat eine maske aufträgt, bin ich auf das prinzip gestoßen vonmaskierte Arrays. Als solches muss nur ein Array verarbeitet werden, da ein maskiertes Array das Bild und die Maskenarrays miteinander vermischt.
Ein kleines Detail zum Maskenarray: Anstatt den inneren Teil (Umfang des Originalbilds) mit Einsen zu füllen und den äußeren Teil (Rand) mit Nullen, wie im vorherigen Update, zu füllen, müssen Sie umgekehrt vorgehen. Eine 1 in einem maskierten Array bedeutet "ungültig", eine 0 bedeutet "gültig".
Dieser Code ist sogar 50% schneller als der in Update 2) bereitgestellte Code:

    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)

-> Ich muss mich hier korrigieren!
Ich muss mich bei der Validierung irren, da es nach einigen Berechnungsläufen so aussahscipy.ndimage.<filters> kann masked_arrays nicht so behandeln, dass die Maske während des Filtervorgangs nicht berücksichtigt wird.
Einige andere Leute erwähnten dies ebenfallsHier undHier.

Die Kraft eines Bildes ...

grau: Umfang des zu verarbeitenden Bildesweiß: gepolsterter Bereich (in meinem Fall mit 2.0 gefüllt)rote Nuancen: Umfang des Kernelsdunkelrot: effektive nachbarschafthellrot: Teil der Nachbarschaft, der ignoriert werden soll

Wie kann dieser eher pragmatische Code geändert werden, um die Leistung der Berechnung zu verbessern?

Vielen Dank im Voraus!

Antworten auf die Frage(2)

Ihre Antwort auf die Frage