2016-08-23 4 views
-1

Ich habe viele 750x750 Bilder. Ich möchte das geometrische Mittel von nicht überlappenden 5x5-Patches von jedem Bild nehmen und dann für jedes Bild die geometrischen Mittel berechnen, um ein Merkmal pro Bild zu erzeugen. Ich habe den Code unten geschrieben und es scheint gut zu funktionieren. Aber ich weiß, dass es nicht sehr effizient ist. Das Ausführen von etwa 300 Bildern dauert etwa 60 Sekunden. Ich habe ungefähr 3000 Bilder. Also, während es für meinen Zweck funktioniert, ist es nicht effizient. Wie kann ich diesen Code verbessern?Wie kann ich diesen Code schneller machen?

#each sublist of gmeans will contain a list of 22500 geometric means 
#corresponding to the non-overlapping 5x5 patches for a given image. 
gmeans = [[],[],[],[],[],[],[],[],[],[],[],[]] 
#the loop here populates gmeans. 
for folder in range(len(subfolders)): 
    just_thefilename, colorsourceimages, graycroppedfiles = get_all_images(folder) 
    for items in graycroppedfiles: 
     myarray = misc.imread(items) 
     area_of_big_matrix=750*750 
     area_of_small_matrix= 5*5 
     how_many = area_of_big_matrix/area_of_small_matrix 
     n = 0 
     p = 0 
     mylist=[] 
     while len(mylist) < how_many: 
      mylist.append(gmean(myarray[n:n+5,p:p+5],None)) 
      n=n+5 
      if n == 750: 
       p = p+5 
       n = 0 
     gmeans[folder].append(my list) 
#each sublist of mean_of_gmeans will contain just one feature per image, the mean of the geometric means of the 5x5 patches. 
mean_of_gmeans = [[],[],[],[],[],[],[],[],[],[],[],[]] 
for folder in range(len(subfolders)): 
    for items in range(len(gmeans[0])): 
     mean_of_gmeans[folder].append((np.mean(gmeans[folder][items],dtype=np.float64))) 
+1

könnte ein guter Kandidat für die Code-Review-Partner-Website. Stellen Sie jedoch sicher, dass Sie zuerst die Regeln für die Website überprüfen. – cel

+0

Das klingt in der Tat sehr langsam für solch eine einfache Operation (ich denke, dass ein Teil von mir Berechnungen bis zur Kumulation der 4. Ordnung und viel mehr wie Histogramm-Entzerrung noch schneller ist). Das erste, was ich tun würde: probiere [scikit-image] (http://scikit-image.org/) aus, was dir eine Funktion namens [view_as_block] (http://scikit-image.org/docs/stable/api) gibt /skimage.util.html#view-as-blocks). Dies wird durch einige erweiterte numpy-Funktionen [as_strided] (http://www.scipy-lectures.org/advanced/advanced_numpy/#example-fake-dimensions-with-strides) implementiert. Ich kann nichts garantieren, aber es könnte viel schneller sein! – sascha

+0

Code-Review hat nicht viel 'numpy' Traffic; während "Vektorisieren" eine reguläre Frage zu SO ist. Auch CR ist wählerischer über das Format. – hpaulj

Antwort

2

Ich kann den Vorschlag verstehen dies auf die Code-Review-Website zu bewegen, aber dieses Problem liefert ein schönes Beispiel für die Macht der numpy und scipy Funktionen vektorisiert verwenden, so dass ich eine Antwort geben.

Die untenstehende Funktion, clever func genannt, berechnet den gewünschten Wert. Der Schlüssel ist, das Bild in ein vierdimensionales Array umzuformen. Dann kann es als ein zweidimensionales Array von zweidimensionalen Arrays interpretiert werden, wobei die inneren Arrays die 5x5 Blöcke sind.

scipy.stats.gmean kann das geometrische Mittel über mehr als eine Dimension berechnen, so dass verwendet wird die vierdimensionale Anordnung zu der gewünschten zweidimensionalen Anordnung von geometrischen Mitteln zu reduzieren. Der Rückgabewert ist der (arithmetische) Mittelwert dieser geometrischen Mittelwerte.

import numpy as np 
from scipy.stats import gmean 


def func(img, blocksize=5): 
    # img must be a 2-d array whose dimensions are divisible by blocksize. 
    if (img.shape[0] % blocksize) != 0 or (img.shape[1] % blocksize) != 0: 
     raise ValueError("blocksize does not divide the shape of img.") 

    # Reshape 'img' into a 4-d array 'blocks', so blocks[i, :, j, :] is 
    # the subarray with shape (blocksize, blocksize). 
    blocks_nrows = img.shape[0] // blocksize 
    blocks_ncols = img.shape[1] // blocksize 
    blocks = img.reshape(blocks_nrows, blocksize, blocks_ncols, blocksize) 

    # Compute the geometric mean over axes 1 and 3 of 'blocks'. This results 
    # in the array of geometric means with size (blocks_nrows, blocks_ncols). 
    gmeans = gmean(blocks, axis=(1, 3), dtype=np.float64) 

    # The return value is the average of 'gmeans'. 
    avg = gmeans.mean() 

    return avg 

Zum Beispiel wird hier die Funktion auf ein Array mit Shape (750, 750) angewendet.

In [358]: np.random.seed(123) 

In [359]: img = np.random.randint(1, 256, size=(750, 750)).astype(np.uint8) 

In [360]: func(img) 
Out[360]: 97.035648309350179 

Es ist nicht leicht zu überprüfen, ob das das richtige Ergebnis ist, so ist hier ein viel kleineres Beispiel:

In [365]: np.random.seed(123) 

In [366]: img = np.random.randint(1, 4, size=(3, 6)) 

In [367]: img 
Out[367]: 
array([[3, 2, 3, 3, 1, 3], 
     [3, 2, 3, 2, 3, 2], 
     [1, 2, 3, 2, 1, 3]]) 

In [368]: func(img, blocksize=3) 
Out[368]: 2.1863131342986666 

Hier ist die direkte Berechnung:

In [369]: 0.5*(gmean(img[:,:3], axis=None) + gmean(img[:, 3:], axis=None)) 
Out[369]: 2.1863131342986666 
+0

Wow. Ich habe gerade meine Funktion mit den 3000 Bildern zeitlich abgestimmt. Ein Keuchhusten 13 Minuten! Deine Funktion - 1s. Tolle. Ich verstehe es nicht total, und ich vertraue deinem Können mehr als meinem Können. Aber, meine Schleife und Ihre Funktion enden mit verschiedenen Werten nach der zweiten Dezimalstelle. Ich frage mich, warum das ist? Ich habe meine Schleife auf einigen kleinen 5x5-Matrizen getestet, und Ihre Funktion ist dieselbe, und wir haben den gleichen Wert. Es scheint, dass etwas mit diesen größeren Matrizen passiert. – dcook

+0

Während ich daran arbeitete, fand ich heraus, dass 'gmean' einige Arten von Castings macht, die nicht mit seinem Docstring übereinstimmen. Übergeben Sie 'gmean' ein Array mit dem Typ' np.uint8' (typisch für ein Bild), hat das Ergebnis den Datentyp 'np.float16'! Ich habe das vermieden, indem ich 'img = np.asarray (img, dtype = np.float64) 'als die erste Zeile von' func() ', aber es stellt sich heraus, dass Sie auch den Datentyp der 'gmean'-Berechnung angeben können, indem Sie das Argument' dtype' verwenden. Versuchen Sie, Ihren 'gmean'-Aufruf in' gmean (myarray [n: n + 5, p: p + 5], Keine, dtype = np.float64) 'zu ändern. Ich habe meine Version aktualisiert, um dies zu verwenden. –

Verwandte Themen