[C#] Les mystères de la représentation mémoire d'un objet Bitmap

Les mystères de la représentation mémoire d'un objet Bitmap [C#] - C#/.NET managed - Programmation

Marsh Posté le 01-06-2006 à 23:11:09    

J'en ai chié comme un rat dépressif mort pendant 3 heures pour remplacer "GetPixel()" qui est très lent par le parcours en mémoire d'un array de byte correspondant aux données brutes d'un objet Bitmap...
 
Seulement, j'ai fini par trouver la solution "par l'oppération du saint esprit", et je ne comprends pas :
- pkoi ça plante pas (en toute logique, je devrais avoir un dépassement mémoire)
- pkoi ça marche (que ça plante pas... là la limite... mais que ça donne le bon résultat, là c'est moi qui fait un stack overflow :o)
- pkoi quand je lit n'importe comment de manière anarchique dans le tableau, j'ai que des couleurs grises, alors que par cette oppération foireusement miraculeuse, ça me donne des couleurs...
 
Des suggestions ?
 

Code :
  1. private void PreProcess()
  2.         {
  3.             int nbRows = (ori.Height * nbCols) / ori.Width;
  4.             int width = ori.Width;
  5.             int height = ori.Height;
  6.             int sWidth = width / nbCols;
  7.             int sHeight = height / nbRows;
  8.             int ptr;
  9.             BitmapData bData = ori.LockBits(new Rectangle(new Point(), ori.Size), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
  10.             // number of bytes in the bitmap
  11.             int byteCount = bData.Stride * ori.Height;
  12.             byte[] bmpData = new byte[byteCount];
  13.             // Copy the locked bytes from memory
  14.             Marshal.Copy(bData.Scan0, bmpData, 0, byteCount);
  15.             // don't forget to unlock the bitmap!!
  16.             ori.UnlockBits(bData);
  17.             // Découpe en colonnes
  18.             for (int i = 0; i < nbCols; i++)
  19.             {
  20.                 // Découpe en lignes
  21.                 for (int j = 0; j < nbRows; j++)
  22.                 {
  23.                     this.pictureBox2.Invalidate();
  24.                     long nbPixels = 0;
  25.                     long cR = 0;
  26.                     long cG = 0;
  27.                     long cB = 0;
  28.                     // Parcoure le carré dans l'image originale
  29.                     for (int x = j * sHeight; x < (j + 1) * sHeight; x++)
  30.                     {
  31.                         // 1) Je panne pas pkoi je dois utiliser "height" ici, alors que le stride correspond à une ligne entière
  32.                         // 2) Je panne encore moins pkoi ça ne plante pas, puisque "ptr" est bien plus grand avec "height" qu'avec "width"
  33.                         // 3) Je panne pas non plus pkoi, alors que j'ai interverti x et y par rapport à la méthode "GetPixel()" j'obtiens la bonne miniature...
  34.                         // 4) Pour en revenir au 2, ça me fait des lignes grises, et pas une seule couleur... Même en n'étant pas au bon offset, ça devrait rammener au moins des couleurs dans le désordre...
  35.                         ptr = (bData.Stride - height * 3) * x;
  36.                         for (int y = i * sWidth; y < (i + 1) * sWidth; y++)
  37.                         {
  38.                             // Stocke les couleurs du pixel pour en faire une moyenne
  39.                             cB += (long)bmpData[ptr + (x * height + y) * 3];
  40.                             cG += (long)bmpData[ptr + (x * height + y) * 3 + 1];
  41.                             cR += (long)bmpData[ptr + (x * height + y) * 3 + 2];
  42. //                            cB += (long)ori.GetPixel(x, y).B;
  43.                             nbPixels++;
  44.                         }
  45.                     }
  46.                     g.FillRectangle(new SolidBrush(Color.FromArgb((int)(cR / nbPixels), (int)(cG / nbPixels), (int)(cB / nbPixels))), i * sWidth, j * sHeight, sWidth, sHeight);
  47.                     //pictureBox2.Refresh();
  48.                 }
  49.             }
  50.         }


 
=> Pour le moment, ça ne fait que pixeliser l'image. Je recopie une image d'une picturebox (objet ori) vers l'image d'un autre picture box en passant par un Graphics (objet g). J'en profite pour faire la moyenne des couleurs dans des rectangles de découpe, puis je dessine un rectangle de la même taille avec la couleur moyenne dans la destination (le but après, ça va être de modifier cet affichage des rectangles pour faire autre chose, donc me sortez pas "laMethodePixeliseQuiTue()", elle ne me servira à rien ;)

Reply

Marsh Posté le 01-06-2006 à 23:11:09   

Reply

Marsh Posté le 01-06-2006 à 23:19:01    

Pour preuve qu'en faisant "total n'importe quoi" ça marche :
http://magicbuzz.multimania.com/files/pigepas.png

Reply

Marsh Posté le 02-06-2006 à 12:51:36    

Y'a que toi pour te plaindre quand ça marche   :D

Reply

Marsh Posté le 02-06-2006 à 15:46:04    

ben ça m'ennuie quand je comprends pas. surtout que là, j'ai besoin de comprendre ce que ça fait, parceque je vais devoir modifier le truc...
et déjà en comprenant ce que je fais, je m'en sors pas, mais alors là, c'est le pompom :D

Reply

Marsh Posté le 02-06-2006 à 16:15:21    

Je serais toi, je rajouterai des variables pour stocker les valeurs calculées.
Ca peut optimiser un pti peu tes boucles, et ça te permettra sans doute de mieux comprendre.
Paske les for(int x = j * sHeight; x < (j + 1) * sHeight; x++)  c'est assez inbitable :)

Reply

Marsh Posté le 02-06-2006 à 16:45:34    

Euh... au départ, sHeight n'existait pas, c'était :
 
(j + 1) * (ori.Height / ((ori.Height * nbCols) / ori.Width))
 
Là, ouais, c'était imbittable ;)

Reply

Marsh Posté le 08-06-2006 à 12:01:54    

Voilà un bout de code qui peut ptet t'intéresser :

Code :
  1. unsafe {
  2.                 // on va charcuter bitmap
  3.                 BitmapData bmd = bitmap.LockBits(
  4.                     new Rectangle(0, 0, bitmap.Width, bitmap.Height)
  5.                     , System.Drawing.Imaging.ImageLockMode.WriteOnly
  6.                     , PixelFormat.Format32bppArgb);
  7.                 // on prend un pointeur sur le début du tableau
  8.                 byte* ptr = (byte*) bmd.Scan0;
  9.                 // on fait un super traitement
  10.                 for(int i=0 ; i<bmd.Height ; i++) {
  11.                     for(int j=0 ; j<bmd.Width ; j++) {
  12.                         *ptr++ = 0; //b
  13.                         *ptr++ = 0; //g
  14.                         *ptr++ = 0; //r  
  15.                         *ptr++ = 0; //a
  16.                     }
  17.                     // comme c'est du 32 bits argb, je sais que un pixel = un quadruple blue/green/red/alpha  
  18.                     // en 24 bits un pixel = un triplet blue/green/red
  19.                     // on a fini une ligne, on passe à la ligne suivante
  20.                     // à cause de la structure des bitmaps, on doit encore avancer un peu histoire de bouffer le padding (cf. image plus bas)
  21.                     ptr += bmd.Stride - bmd.Width * 4; // en 24bits ca serait 3 et pas 4...
  22.              
  23.                 }
  24.                 bitmap.UnlockBits(bmd);
  25.             }


 
:o
 
http://www.bobpowell.net/lockin1.gif


Message édité par Xavier_OM le 08-06-2006 à 12:04:14

---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 08-06-2006 à 12:03:03    

[:drapal]

Reply

Marsh Posté le 08-06-2006 à 12:55:14    

1/ pkoi le +4 ?
2/ ouais, mais justement, si tu regardes mon code, mes interrogations sont en fonction du schéma que tu as posté : à moins que je me sois complètement planté dans le nommage de mes variables (possible) j'ai une incohérence complète : je bouge mon pointeur à partir de la largeur, mais ce sont des colonnes que je récupère, et non ds lignes. du coup, je lis beaucoup trop de fois une taille trop grande (donc ça devrait déborder) et pourtant je récupère bien mon image, alors qu'en toute logique elle devrait être complètement déterriorrée (problèmes d'offsets). hors ça marche :spamafote:

Reply

Marsh Posté le 08-06-2006 à 13:58:51    

1)  :??:  
2) Ya plein de truc que je capte pas trop dans ton code :
Ca par exemple :
int nbRows = (ori.Height * nbCols) / ori.Width;
 
Et ca ? ca te file le nombre de pixels (zone de padding compris, ce qui n'a pas de sens) mais pas le nombre de bytes  :??:  
int byteCount = bData.Stride * ori.Height;


---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 08-06-2006 à 13:58:51   

Reply

Marsh Posté le 08-06-2006 à 14:13:17    

Code :
  1. // on a fini une ligne, on passe à la ligne suivante
  2. // à cause de la structure des bitmaps, on doit encore avancer un peu histoire de bouffer le padding (cf. image plus bas)
  3. ptr += bmd.Stride - bmd.Width * 4;
  4. // en 24bits ca serait 3 et pas 4...


 
Moi je fais pas le +4 et ça marche quand même :sol:
Et pourtant, y'a pas 4 d'écart entre le width et le height de mon image :D
 
:pt1cable:

Message cité 1 fois
Message édité par Arjuna le 08-06-2006 à 14:13:54
Reply

Marsh Posté le 08-06-2006 à 14:17:04    

Xavier_OM a écrit :


2) Ya plein de truc que je capte pas trop dans ton code :
Ca par exemple :
int nbRows = (ori.Height * nbCols) / ori.Width;


Parceque je découpe mon image en petits rectangles avant le traitement.
(à la base, je vais faire des traîtements sur ces rectangles de découpe).
 
Là, nbCols est mettons 10.
Vu que je veux des rectangles "carrés", je récupère nbRows en fonction de la largeur et de la hauteur (ratio).
 
C'est vrai que mes boucles imbriquées ne sont pas terrible pour aider à la compréhesion...
 
En fait, je recopie l'image par petits rectangles, et non pas de façon séquencielle.

Reply

Marsh Posté le 08-06-2006 à 14:17:08    

Arjuna a écrit :

Code :
  1. // on a fini une ligne, on passe à la ligne suivante
  2. // à cause de la structure des bitmaps, on doit encore avancer un peu histoire de bouffer le padding (cf. image plus bas)
  3. ptr += bmd.Stride - bmd.Width * 4;
  4. // en 24bits ca serait 3 et pas 4...


 
Moi je fais pas le +4 et ça marche quand même :sol:
Et pourtant, y'a pas 4 d'écart entre le width et le height de mon image :D
 
:pt1cable:


 
 
Je fais pas +4 hein  :heink:  
La longueur d'une ligne en bytes c'est stride, la longueur de mon image en pixels c'est width, donc en bytes pour du 32bits c'est 4*width
D'où un padding de stride - 4*width
D'où j'avance de cette quantité pour me placer sur la ligne suivante


---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 08-06-2006 à 14:18:47    

Sinon, je ne sais pas d'où tu sors ce padding en fait.
 
Le "bytesCount" (et tout le bloc qui va avec) c'est un copy/paste à partir d'un site style "csharpcorner".
je me demande même si j'ai pas vu le même truc sur la MSDN.
 
PS: je suis en .NET 2.0 ici. Au cas où ça changer d'une version à l'autre (ce serait bien con mais bon :D)

Reply

Marsh Posté le 08-06-2006 à 14:20:30    

D'ailleurs, en re-regardant ton graph, c'est toi qu'est pas logique.
 
stride = (with * nbBytespp) + padding
 
donc stride * height est bel et bien égal au nombre de bytes correspondant à l'image mémoire entière du bitmap... y'a pas à tortiller à ce niveau...
 
du coup, en bon français : (pour une image 24 bits)
 


pour ligne = 0, tant que ligne < hauteur, incrément de 1
      pour colonne = 0, tant que colonne < largeur, incrément de 1
           pixelCouleurRouge = img[ligne * stride + colonne * 3]
           pixelCouleurVerte = img[ligne * stride + colonne * 3 + 1]
           pixelCouleurBleue = img[ligne * stride + colonne * 3 + 2]
      fin pour
fin pour


Message édité par Arjuna le 08-06-2006 à 14:25:03
Reply

Marsh Posté le 08-06-2006 à 14:25:23    

MSDN :  
"The stride is the width of a single row of pixels (a scan line), rounded up to a four-byte boundary. The stride is always greater than or equal to the actual pixel width. If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up."
 
 
désolé mais faut lire la doc hein :o

Message cité 1 fois
Message édité par Xavier_OM le 08-06-2006 à 14:25:51

---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 08-06-2006 à 14:26:19    

ben oui, donc les 4 pixels ne sont pas "en plus" du stride, il sont déjà dans le stride...
 
dans ton exemple de code de là-haut, y'a une erreur avec le 4...

Reply

Marsh Posté le 08-06-2006 à 14:27:11    

correction : nan, y'a pas d'erreur. juste IE 7 béta 2 qui écrit la balise "code" trop petit : je voyais un + 4 et non un * 4
* 4, je suis d'accord :D

Reply

Marsh Posté le 08-06-2006 à 14:27:52    

Arjuna a écrit :

ben oui, donc les 4 pixels ne sont pas "en plus" du stride, il sont déjà dans le stride...
 
dans ton exemple de code de là-haut, y'a une erreur avec le 4...


 
j'abandonne, visiblement tu ne sais pas lire  :o


---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 08-06-2006 à 14:29:29    

Xavier_OM a écrit :

MSDN :  
"The stride is the width of a single row of pixels (a scan line), rounded up to a four-byte boundary. The stride is always greater than or equal to the actual pixel width. If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up."
 
 
désolé mais faut lire la doc hein :o


up to, ça veut dire "jusqu'à" donc c'est pas forcément 4.
mais vu que c'est pas un + mais un * qu'il y a dans ton code, je suis d'accord. moi je croyais que tu ajoutait la taille du stride + 4 dans l'offset

Reply

Marsh Posté le 08-06-2006 à 14:30:02    

Xavier_OM a écrit :

j'abandonne, visiblement tu ne sais pas lire  :o


c'est toi qui veut pas comprendre que IE7b2 est trop con pour rendre lisible la balise CODE

Reply

Marsh Posté le 08-06-2006 à 14:30:18    

Arjuna a écrit :

up to, ça veut dire "jusqu'à" donc c'est pas forcément 4.
mais vu que c'est pas un + mais un * qu'il y a dans ton code, je suis d'accord. moi je croyais que tu ajoutait la taille du stride + 4 dans l'offset


 
change de browser alors ;)


---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 08-06-2006 à 14:37:45    

bah moz y déconne (c'est une béta 2 aussi :D)

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed