191 votes

Comment normaliser un tableau NumPy à l'intérieur d'une certaine plage ?

Après avoir effectué un traitement sur un tableau audio ou image, il doit être normalisé dans une plage avant de pouvoir être réécrit dans un fichier. Cela peut être fait comme suit :

# Normalize audio channels to between -1.0 and +1.0
audio[:,0] = audio[:,0]/abs(audio[:,0]).max()
audio[:,1] = audio[:,1]/abs(audio[:,1]).max()

# Normalize image to between 0 and 255
image = image/(image.max()/255.0)

Existe-t-il un moyen moins verbeux et plus pratique de le faire ? matplotlib.colors.Normalize() ne semble pas être lié.

181voto

unutbu Points 222216
audio /= np.max(np.abs(audio),axis=0)
image *= (255.0/image.max())

Utilisation de /= y *= permet d'éliminer un tableau temporaire intermédiaire, ce qui permet d'économiser de la mémoire. La multiplication est moins coûteuse que la division, donc

image *= 255.0/image.max()    # Uses 1 division and image.size multiplications

est marginalement plus rapide que

image /= image.max()/255.0    # Uses 1+image.size divisions

Puisque nous utilisons ici des méthodes numpy de base, je pense que cette solution est à peu près aussi efficace que possible en numpy.


Les opérations en place ne modifient pas le dtype du tableau conteneur. Étant donné que les valeurs normalisées souhaitées sont des flottants, la fonction audio y image Les tableaux doivent avoir un type de virgule flottante avant que les opérations in-place ne soient effectuées. S'ils ne sont pas déjà de type virgule flottante, vous devrez les convertir en utilisant la fonction astype . Par exemple,

image = image.astype('float64')

7 votes

Pourquoi la multiplication est-elle moins coûteuse que la division ?

21 votes

Je ne sais pas exactement pourquoi. Cependant, je suis sûr de cette affirmation, l'ayant vérifiée avec timeit. Avec la multiplication, vous pouvez travailler avec un chiffre à la fois. Avec la division, surtout avec des diviseurs importants, vous devez travailler avec de nombreux chiffres et "deviner" combien de fois le diviseur entre dans le dividende. Vous finissez par faire de nombreux problèmes de multiplication pour résoudre un seul problème de division. L'algorithme informatique de la division n'est peut-être pas le même que celui de la division longue humaine, mais je pense néanmoins qu'il est plus compliqué que la multiplication.

16 votes

Il est probablement utile de mentionner un diviseur par zéro pour les images vierges.

108voto

Tactopoda Points 563

Si le tableau contient des données positives et négatives, je choisirais :

import numpy as np

a = np.random.rand(3,2)

# Normalised [0,1]
b = (a - np.min(a))/np.ptp(a)

# Normalised [0,255] as integer: don't forget the parenthesis before astype(int)
c = (255*(a - np.min(a))/np.ptp(a)).astype(int)        

# Normalised [-1,1]
d = 2.*(a - np.min(a))/np.ptp(a)-1

Si le tableau contient nan Une solution pourrait être de les supprimer tout simplement :

def nan_ptp(a):
    return np.ptp(a[np.isfinite(a)])

b = (a - np.nanmin(a))/nan_ptp(a)

Cependant, selon le contexte, vous pourriez vouloir traiter nan différemment. Par exemple, interpoler la valeur, en la remplaçant par 0, par exemple, ou soulever une erreur.

Enfin, cela vaut la peine d'être mentionné, même si ce n'est pas la question de l'OP, normalisation :

e = (a - np.mean(a)) / np.std(a)

2 votes

Selon ce que vous voulez, ce n'est pas correct, car cela renverse les données. Par exemple, la normalisation à [0, 1] place le maximum à 0 et le minimum à 1. Pour [0, 1], vous pouvez simplement soustraire le résultat de 1 pour obtenir la normalisation correcte.

0 votes

Merci de le signaler @AlanTuring, c'était très négligé. Le code, tel que posté, ne fonctionnait QUE si les données contenaient des valeurs positives et négatives. Cela peut être assez courant pour des données audio. Cependant, la réponse est mise à jour pour normaliser toutes les valeurs réelles.

1 votes

Le dernier est également disponible en tant que scipy.stats.zscore .

42voto

cjohnson318 Points 509

Vous pouvez également changer d'échelle en utilisant sklearn . Les avantages sont que vous pouvez ajuster la normalisation de l'écart type, en plus de centrer la moyenne des données, et que vous pouvez le faire sur l'un ou l'autre axe, par caractéristiques ou par enregistrements.

from sklearn.preprocessing import scale
X = scale( X, axis=0, with_mean=True, with_std=True, copy=True )

Les arguments des mots-clés axis , with_mean , with_std sont explicites, et sont affichés dans leur état par défaut. L'argument copy effectue l'opération sur place s'il est défini comme suit False . Documentation aquí .

0 votes

X = scale( [1,2,3,4], axis=0, with_mean=True, with_std=True, copy=True ) me donne une erreur

0 votes

X = scale( np.array([1,2,3,4]), axis=0, with_mean=True, with_std=True, copy=True ) me donne un tableau de [0,0,0,0].

0 votes

Sklearn.preprocessing.scale() a le revers de la médaille que vous ne savez pas ce qui se passe. Quel est le facteur ? Quelle compression de l'intervalle ?

16voto

yellow01 Points 582

Vous essayez de mettre à l'échelle minimale et maximale les valeurs de audio entre -1 et +1 et image entre 0 et 255.

Utilisation de sklearn.preprocessing.minmax_scale devrait facilement résoudre votre problème.

Par exemple :

audio_scaled = minmax_scale(audio, feature_range=(-1,1))

y

shape = image.shape
image_scaled = minmax_scale(image.ravel(), feature_range=(0,255)).reshape(shape)

note : A ne pas confondre avec l'opération qui met à l'échelle la norme (longueur) d'un vecteur à une certaine valeur (généralement 1), ce qui est aussi communément appelé normalisation.

11voto

u0b34a0f6ae Points 14874

Vous pouvez utiliser la version "i" (comme dans idiv, imul..), et cela ne semble pas si mal :

image /= (image.max()/255.0)

Dans l'autre cas, vous pouvez écrire une fonction pour normaliser un tableau à n dimensions par des colonnes :

def normalize_columns(arr):
    rows, cols = arr.shape
    for col in xrange(cols):
        arr[:,col] /= abs(arr[:,col]).max()

0 votes

Pouvez-vous clarifier ceci ? Les parenthèses le font se comporter différemment que sans ?

1 votes

Les paranthèses ne changent rien. le but était d'utiliser /= au lieu de = .. / ..

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X