Pagina 1 di 3 1 2 3 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 27

Hybrid View

  1. #1
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308

    [c#] Leggere direttamente una bitmap senza portarla in memoria

    Ho seguito il (saggio) consiglio di MItaly ed ho scritto un codice che copia un rectangle da una bitmap gigantesca b1 in una seconda bitmap b2 in memoria. Tutto funziona bene MA ad una condizione: il Width della b1 DEVE essere un multiplo di 4, altrimenti succede quello che potete osservare nel test1 allegato. Allora ho fatto uno studio sull'head del file b1 prima e dopo aver tolto una colonna e quindi portare il Width di b1 da un valore multiplo di 4 ad un valore che non è multiplo di 4 (test2 allegato). Praticamente non cambia nulla di clamoroso se non il valore Width al byte n.18 che il mio codice tra l'altro legge bene e (stranamente) non cambia la dimensione totale in byte dell'immagine (byte n. 34) anche se ho effettuato un crop. A cosa può essere dovuto quel disastro? Perchè sono costretto ad usare Width forzati a multipli di 4 quando esistono bitmap di ogni dimensione? Non credo di aver sbagliato la funzione del puntatore (int) Ptr al byte nel mio codice perchè intanto funziona con le bitmap che hanno un Width multiplo di 4 ed inoltre, prima e dopo il crop, l'offset da cui partono i dati pixel dell'immagine (byte n.10) non è cambiato. Qualche idea?

    codice:
                string path = "gabri.bmp";
                Vector4 Data = GetData(path);
                int Dim1 = (int)Data.X;
                int Dim2 = (int)Data.Y;
                int Bits = (int)Data.Z;
                int Ofs = (int)Data.W;
                byte[] a = GetAllByte(path);
                int x0 = 300; 
                int y0 = 50; 
                int dimx = 200; 
                int dimy = 100; 
                Bitmap b2 = new Bitmap(dimx, dimy);
                int Ptr=0, R, G, B;
                for(int y=y0; y < y0 + dimy; y++)
                    for (int x = x0; x < x0 + dimx; x++)
                    {
                        Ptr = Ofs + (Bits / 8) * (x + Dim1 * (Dim2 - y - 1));
                        B = Convert.ToInt32(a[Ptr]);
                        G = Convert.ToInt32(a[Ptr+1]);
                        R = Convert.ToInt32(a[Ptr+2]);
                        b2.SetPixel(x - x0, y - y0, System.Drawing.Color.FromArgb(R, G, B));
                    }
                float ratio = 500f / Dim1;
                if (Dim2 * ratio > 540)
                    ratio = 540f / Dim2;
                pictureBox1.Width = (int)(Dim1 * ratio);
                pictureBox1.Height = (int)(Dim2 * ratio);
                pictureBox1.Visible = true;
                pictureBox1.Image = b2;
                b2.Save("b2.bmp");
    codice:
           public static Vector4 GetData(string path)
            {
                FileStream x = File.Open(path, FileMode.OpenOrCreate);
                x.Seek(0, SeekOrigin.Begin);  
                byte[] b = new byte[30];
                x.Read(b, 0, b.Length);
                x.Close();
                int offset = Convert.ToInt32(b[10]);
                int Dim1 = Convert.ToInt32(b[18]) + Convert.ToInt32(b[19]) * 256;
                int Dim2 = Convert.ToInt32(b[22]) + Convert.ToInt32(b[23]) * 256;
                int bits = Convert.ToInt32(b[28]);
                return new Vector4(Dim1, Dim2, bits, offset);
            }
    codice:
            public static byte[] GetAllByte(string path)
            {
                FileStream f = File.Open(path, FileMode.OpenOrCreate);
                f.Seek(0, SeekOrigin.Begin);  
                byte[] a = new byte[f.Length];
                f.Read(a, 0, a.Length);
                f.Close();
                return a;
            
            }
    Immagini allegate Immagini allegate
    Ultima modifica di escocat; 16-02-2015 a 16:01

  2. #2
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    allegati
    Immagini allegate Immagini allegate

  3. #3
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    AHI. Il mio codice mi ha aiutato a capire una cosa spaventosa: in effetti le bitmap sono TUTTE con Width multipli di 4. Se io tolgo qualche colonna per avere un Width "spurio" il maledetto sistema operativo mi rimette le colonne che ho tolto riempendole di pixel NERI.

  4. #4
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Ecco il codice PERFETTO:

    codice:
                string path = "gabri.bmp";
                Vector4 Data = GetData(path);
                int Dim1 = (int)Data.X;
                int Dim2 = (int)Data.Y;
                int Bits = (int)Data.Z;
                int Ofs = (int)Data.W;
                byte[] a = GetAllByte(path);
                int x0 = 300; 
                int y0 = 50; 
                int dimx = 200; 
                int dimy = 100; 
    
                //Se Dim1 non è multiplo di 4 lo incrementa del minimo  
                //termine necessario per essere multiplo di 4
                int truewidth;
                float fd = 1f*Dim1/4f;
                int id = (int)(Dim1/4);
                if (fd == (float)id)
                    truewidth = Dim1;
                else
                    truewidth = (id + 1) * 4;
    
                Bitmap b2 = new Bitmap(dimx, dimy);
                int Ptr=0, R, G, B;
                for(int y=y0; y < y0 + dimy; y++)
                    for (int x = x0; x < x0 + dimx; x++)
                    {
                        Ptr = Ofs + (Bits / 8) * (x + truewidth * (Dim2 - y - 1));
                        B = Convert.ToInt32(a[Ptr]);
                        G = Convert.ToInt32(a[Ptr+1]);
                        R = Convert.ToInt32(a[Ptr+2]);
                        b2.SetPixel(x - x0, y - y0, System.Drawing.Color.FromArgb(R, G, B));
                    }
                float ratio = 500f / Dim1;
                if (Dim2 * ratio > 540)
                    ratio = 540f / Dim2;
                pictureBox1.Width = (int)(Dim1 * ratio);
                pictureBox1.Height = (int)(Dim2 * ratio);
                pictureBox1.Visible = true;
                pictureBox1.Image = b2;
                b2.Save("b2.bmp");

  5. #5
    Anche se la "larghezza logica" di una .BMP può essere qualunque cosa, la lunghezza effettiva di ogni scanline è multipla di 4 byte per evitare accessi non allineati alla memoria (la stride viene arrotondata ai 4 byte successivi aggiungendo byte di padding).
    Amaro C++, il gusto pieno dell'undefined behavior.

  6. #6
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Ma quale codice perfetto, funziona sempre e soltanto quando il width è un multiplo di 4! Mi sembrava troppo bello, meno male che io lavoro sempre con HighMap multiple di 4 (anzi di 64) però ora mi sono intestardito. In effetti QUALCHE VOLTA aggiunge le colonne nere per arrivare ad un multiplo di 4, altre volte in effetti fa cose turche. Ma come disse Einstein "Dio è capriccioso ma non malizioso" una logica è lì che aspetta di essere scoperta. Sto provando a costruire bitmap di 4x3, 4x4, 5x4, 6x4 ecc. e leggendo tutti i byte voglio capire che cosa hanno in testa i creatori di sistemi operativi.

  7. #7
    Non c'è nulla di magico, semplicemente la lunghezza della scanline viene sempre arrotondata al successivo multiplo di 4 byte... In ogni caso, non c'è bisogno di fare strani esperimenti, le specifiche del formato BMP sono facilmente reperibili in rete.
    Amaro C++, il gusto pieno dell'undefined behavior.

  8. #8
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Ho scoperto che quel lavoro di aggiungere una colonna di neri lo fa solo quando gli manca una colonna per arrivare ad avere un Width multiplo di 4. Quando gliene mancano due o tre fa cose ignobili. Cosa intendi per "specifiche"? Io ho solo trovato la mappatura della firma e dell'intestazione, cioè dei 54 bytes che precedono l'immagine vera e propria (i pixel). Hai qualche link da darmi?

  9. #9
    http://en.wikipedia.org/wiki/BMP_file_format
    In particolare http://en.wikipedia.org/wiki/BMP_fil...#Pixel_storage
    Padding bytes (not necessarily 0) must be appended to the end of the rows in order to bring up the length of the rows to a multiple of four bytes. When the pixel array is loaded into memory, each row must begin at a memory address that is a multiple of 4. This address/offset restriction is mandatory only for Pixel Arrays loaded in memory. For file storage purposes, only the size of each row must be a multiple of 4 bytes while the file offset can be arbitrary.[5] A 24-bit bitmap with Width=1, would have 3 bytes of data per row (blue, green, red) and 1 byte of padding, while Width=2 would have 2 bytes of padding, Width=3 would have 3 bytes of padding, and Width=4 would not have any padding at all.
    Amaro C++, il gusto pieno dell'undefined behavior.

  10. #10
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    HAI CAPITO! Dunque l'aggiustamento a multiplo di 4 non è relativo all'unità pixel ma al byte! E quindi la funzione per il puntatore al byte che indica l'inizio di un pixel non deve dipendere dal Width ma da una lunghezza riga espressa in byte che può anche essere spuria rispetto al numero di pixel. Infatti per Width=9 mi dà una riga di 32 byte che non può contenere un numero intero di pixel, ne contiene 32/3. Allora ci sono byte in più con valori che non esprimono alcun colore di pixel. INGEGNOSO! Ora posso gestire qualsiasi enorme immagine senza occupare un solo byte di memoria. Sicuramente è così che faranno quelli della NASA e di Google Heart. Grazie MItaly, fra te ,Oregon e Alka non so chi è il migliore!

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.