probl=c3=a8me de copie =3f shallow - deep

Le
Fabrice
Bonjour,

je n'ai toujours pas compris on dirait ;-)
Voici le code qui marche et qui simule le reflet d'une image dans l'eau:

import matplotlib.pyplot as mpl # mpl sera notre repère
import numpy as np # np manipule des tableaux
import copy

originale = mpl.imread("damier.jpeg") # un image est lue
image = copy.copy(originale) # image est une copie modifiable
hauteur = len(image) # hauteur contient le nombre de ligne
largeur = len(image[0]) # largeur contient le nombre de pixel

for ligne in range(hauteur//2):
tmp = copy.copy(image[ligne])
image[ligne] = image[hauteur-ligne-1]
image[hauteur-ligne-1] = tmp

print("L'originale")
mpl.imshow(originale,cmap='gray') # On affiche l'image dans le repère
mpl.show() # On affiche le repère à l'écran
print("et son reflet dans l'eau")
mpl.imshow(image,cmap='gray') # On affiche l'image dans le repère
mpl.show()


mais si je ne fais pas copy.copy, je perds la liste dans tmp.

Pourtant, tmp et image[ligne] sont des étiquettes qui pointent sur la
même liste.
Quand je fais image[ligne] = image[hauteur-ligne-1], je détache
l'étiquette image[ligne] de la liste dans tmp pour la coller sur la
liste image[hauteur-ligne-1]. l'étiquette tmp devrait pointer sur la
liste en mémoire qui ne doit ^pas être effacée puisque référencée.
Puis je détache l'étiquette image[hauteur-ligne-1] de la liste dans
image[ligne] maintenant pour l'attacher à la liste dans tmp.

En C++, j'aurai dit que j'ai échangé les pointeurs. Un objet mutable,
c'est bien un pointeur ?

Je ne comprends plus ce que je croyais avoir compris.

Cordialement,
Fabrice.


L'absence de virus dans ce courrier électronique a été vérifiée par le logiciel antivirus Avast.
https://www.avast.com/antivirus
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
marc.marc
Le #26462863
Bonsoir,
en effet comme le dit Alain l'objet retourné par mpl.imread est un array
numpy. Si vous faites:
print(originale.__class__) la réponse est print(originale.shape) la réponse est par exemple (800,800,3), un
tableau tri dimensionnel, en fait 3 images de 800x800 pixels (ici) qui
correspondent aux valeurs rouge, vert, bleu (RGB en anglais) de l'image
originale - bon ça peut dépendre de l'image originale.
Même si l'utilisation du premier indice seul marche dans votre cas, il
vaut mieux de mon point de vue utiliser les 3 indices, par exemple :
for ligne in range(hauteur//2):
tmp = copy.copy(image[ligne,:,:])
image[ligne,:,:] = image[hauteur-ligne-1,:,:]
image[hauteur-ligne-1,:,:] = tmp
Vous pouvez vous amuser à remplacer le dernier : de chaque tableau par
l'entier 2 et vous verrez que l'effet miroir ne s'est fait que sur la
composante bleue de l'image.
Quand on écrit : tmp = image[ligne] travailler sur tmp ou sur
image[ligne] c'est pareil je suis d'accord avec Alain.
Pour créer une nouvelle instance d'array numpy on peut utiliser
copy.copy ou bien np.copy ( peut-être y-a-t-il une différence je ne sais
pas. J'utilise le second ).
Mais écrire :
tmp = image[ligne]*1
produit aussi un nouvel array ( mais personnellement je préfère la copie
explicite avec np.copy )
Quand on écrit : image[ligne,:,:] = image[hauteur-ligne-1,:,:] on
remplace le contenu de la ligne numéro "ligne" de l'image par celui de
la ligne numéro "hauteur-ligne-1", et ce pour chacune des 3 images RGB.
Il ne s'agit pas de pointeurs.
De la même manière que pour un tableau 1D en C si on écrit a[3] = a[4],
même si a est un pointeur, sauf que là c'est à 3 ( généralement à N )
dimensions. En C sauf erreur il faudrait un tableau de tableaux de
tableaux qu'il faudrait allouer dynamiquement par une triple boucle et
il faudrait une double boucle pour remplacer le contenu d'une ligne par
une autre pour chacune des 3 images. Par contre en Fortran moderne (>=
90 ou 2003 je ne sais plus) on peut utiliser le même genre d'expressions
qu'en python/numpy et l'allocation des tableaux à N dimensions se fait
en une ligne. Mais je m'égare ...
Amicalement.
Marc

Le 01/02/2018 à 18:34, Alain Ketterlin a écrit :
Fabrice
je n'ai toujours pas compris on dirait ;-)
Voici le code qui marche et qui simule le reflet d'une image dans l'eau:
import matplotlib.pyplot as mpl # mpl sera notre repère
import numpy as np # np manipule des tableaux
import copy
originale = mpl.imread("damier.jpeg") # un image est lue
image = copy.copy(originale) # image est une copie modifiable
hauteur = len(image) # hauteur contient le nombre de ligne
largeur = len(image[0]) # largeur contient le nombre de pixel
for ligne in range(hauteur//2):
tmp = copy.copy(image[ligne])
image[ligne] = image[hauteur-ligne-1]
image[hauteur-ligne-1] = tmp
print("L'originale")
mpl.imshow(originale,cmap='gray') # On affiche l'image dans le repère
mpl.show() # On affiche le repère à l'écran
print("et son reflet dans l'eau")
mpl.imshow(image,cmap='gray') # On affiche l'image dans le repère
mpl.show()
mais si je ne fais pas copy.copy, je perds la liste dans tmp.

En fait, il ne faut pas considérer les np.array (ou image) comme des
listes Python. Ce sont des structures différents, même si la syntaxe est
similaire.
Si j'ai bien compris (mais marc.marc pourra confirmer -- ou pas), quand
tu écris :
tmp = image[ligne]
tu récupères une "view" sur l'image originale. Ce n'est pas une simple
référence, et de toute façon image[ligne] n'existe pas indépendamment du
reste l'image (les np.array sont "denses", alors que les listes de
listes sont "creuses", ou éclatées).
Donc : 1) les lignes n'existent pas individuellement, 2) tmp et
image[ligne] sont effectivement la même chose, et donc 3)
image[ligne]=... modifie effectivement aussi bien l'image que tmp.
(Je m'arrête là, parce que je ne peux pas essayer en ce moment...)
Pourtant, tmp et image[ligne] sont des étiquettes qui pointent sur la
même liste.
Quand je fais image[ligne] = image[hauteur-ligne-1], je détache
l'étiquette image[ligne] de la liste dans tmp pour la coller sur la
liste image[hauteur-ligne-1]. l'étiquette tmp devrait pointer sur la
liste en mémoire qui ne doit ^pas être effacée puisque référencée.
Puis je détache l'étiquette image[hauteur-ligne-1] de la liste dans
image[ligne] maintenant pour l'attacher à la liste dans tmp.
En C++, j'aurai dit que j'ai échangé les pointeurs. Un objet mutable,
c'est bien un pointeur ?

Ton analyse s'applique bien aux listes python d'origine.
Mais numpy c'est une tout autre bestiole... C'est fait pour faire du
calcul scientifique, donc dans des matrices denses, comme on fait dans
les langages raisonnablement efficaces pour ça (Fortran, C and co).
Numpy, c'est le renard (C/Fortran/...) qui entre dans le poulailler
(celui qui est plein de canards).
-- Alain.
Fabrice
Le #26463002
Merci Alain et Marc,
j'ai trouvé, grâce à vous, une solution pour garder les copies à la
Python ;-)
image = copy.copy(originale).tolist()
Beaucoup d'implicite dans ce langage ;-)
Cordialement,
Fabrice.
---
L'absence de virus dans ce courrier électronique a été vérifiée par le logiciel antivirus Avast.
https://www.avast.com/antivirus
Nicolas
Le #26463397
Bonjour,
Le 02/02/2018 à 18:09, Fabrice a écrit :
Merci Alain et Marc,
j'ai trouvé, grâce à vous, une solution pour garder les copies à la
Python ;-)
image = copy.copy(originale).tolist()
Beaucoup d'implicite dans ce langage ;-)

Pour une liste simple :
l1 = [0, 1, 2, 3]
On peut la copier avec la fonction copy() mais on peut aussi faire :
l2 = list(l1)
C'est plus élégant. Et pas besoin d'importer copy().
Nicolas
Cordialement,
Fabrice.
---
L'absence de virus dans ce courrier électronique a été vérifiée par le
logiciel antivirus Avast.
https://www.avast.com/antivirus
marc.marc
Le #26463435
Dans le code de Fabrice "originale" n'est pas une liste mais un array
numpy à 3 dimensions.
Si l'idée est de transfomer cet array en liste de listes de listes ( ce
dont je ne vois pas trop l'intérêt cela dit) :
image = originale.tolist()
suffira, la copie est implicite. Comme l'indique la doc de tolist:
Return a copy of the array data as a (nested) Python list.
Data items are converted to the nearest compatible Python type.
Par contre list(originale) retourne une liste contenant un elément
unique qui est l'array "originale".
Marc
Le 05/02/2018 à 09:43, Nicolas a écrit :
Bonjour,
Le 02/02/2018 à 18:09, Fabrice a écrit :
Merci Alain et Marc,
j'ai trouvé, grâce à vous, une solution pour garder les copies à la
Python ;-)
image = copy.copy(originale).tolist()
Beaucoup d'implicite dans ce langage ;-)

Pour une liste simple :
l1 = [0, 1, 2, 3]
On peut la copier avec la fonction copy() mais on peut aussi faire :
l2 = list(l1)
C'est plus élégant. Et pas besoin d'importer copy().
Nicolas
Cordialement,
Fabrice.
---
L'absence de virus dans ce courrier électronique a été vérifiée par le
logiciel antivirus Avast.
https://www.avast.com/antivirus

Publicité
Poster une réponse
Anonyme