Oppure

Loading
07/07/21 14:04
Carlo
Quando dichiari la larghezza dell'immagine devi inserire un numero disparo, perché per avere un'immagine larga 3000 pixel = 0...2999, oppure bisogna aggiustare i calcoli dell'indice.

Ma per avere un'immagine a tono continuo, non ti serve più calcolare l'indice perché il vettore lo devi riempire tutto con lo stesso valore 0...255, è sufficiente un unico ciclo che scansiona tutto il vettore:
For ind = 0 To bytes - 1 ' la lunghezza del vettore
     colorValues(ind) = 200 ' un valore qualsiasi 0...255
Next


Per il tono continuo è semplice usare anche il 4bpp, hai tutte le info per modificare il codice, se ti serve prova. :k:
Ultima modifica effettuata da Carlo 07/07/21 21:25
in programmazione tutto è permesso
08/07/21 7:36
bernie
Ciao Carlo
Abbastanza chiaro.
Sicuramente avere file a 4bpp invece che a 8 , significherebbe salvare spazio e considerando che i files che vanno gestiti sono grandi , questo non guasterebbe . Ho provato a pensarci,ma non sono arrivato a niente di fatto .
Ti ricorderai che tempo fa avevo aperto un post dove chiedevo supporto per fare il paste di due files .
Visto che i due files sono uno bianco e il secondo un campo pieno , credo che con questo sistema che mi hai mostrato e spiegato posso "colorare" direttamente il file senza bisogno di incollarne due . Fatto ciò , guarderò anche come fare i file a 4bpp.
Grazie

aaa
08/07/21 12:08
Carlo
Prova, prendendo il mio primo esempio postato, devi aggiungere la creazione della palette con 16 livelli di grigio e modificare il for per riempire il vettore, per inserire il valore usa l'esadecimale, così eviti il calcolo per i due pixel uguali per ogni byte, es. vuoi il livello5: &H55, vuoi il livello 15: &HFF, se non riesci posta il tentativo.

Farei anche una prova senza impostare la palette, non è detto che il sistema grafico che usi ne tenga conto.

Ricordo la richiesta, se avessi spiagato cosa ti serviva, forse ti avrei risparmiato il codice per la fusione, ma comunque valido per imparare.
Ultima modifica effettuata da Carlo 08/07/21 12:12
in programmazione tutto è permesso
08/07/21 13:52
bernie
Hai ragione, ma purtroppo la necessità di modificare la palette è venuta in seguito.
Adesso provo a 4bpp.
Grazie
aaa
12/07/21 8:03
bernie
Ho modificato l'esempio di Carlo per adattarlo alle mie necessità, creare un file bmp con le caratteristiche volute .
A questo punto vorrei provare ad alzare l'asticella, consideriamo di avere un file 100X100 pixel ,che il file debba essere completamente di colore bianco e poi disegnare un rettangolo 70X70 pixel con origine in x=30 y=30 di dimensioni note e di colore voluto .

Avevo pensato una cosa del genere , funziona , però penso che si potrebbe ottimizzare.


        ' puntatore all'indirizzo di memoria del primo byte.
        Dim ptr As IntPtr = bmpData.Scan0

        ' un vettore che conterrà tutti i bytes della bitmap.
        Dim bytes As Long = Math.Abs(bmpData.Stride) * immagine.Height        ' calcolo dei bytes necessari

        Dim colorValues(bytes - 1) As Byte
        Dim ind As Long
        Dim app As Long
        Dim app2 As Long

        app = (larghezza *(altezza-altezza1)                             ' calcolo l'area bianca superiore

        For ind = 0 To app - 1                                                  ' coloro di bianco l'area bianca superiore
            colorValues(ind) = 255
        Next
        For app2 = app To bytes Step Math.Abs(bmpData.Stride)
            For ind = app2 To app2 + (larghezza-larghezza1) - 1         ' coloro di bianco la fascia sinistra
                colorValues(ind) = 255
            Next
        For ind = app2 + (larghezza-larghezza1) - 1 To (app2 + larghezza) - 1  
                colorValues(ind) = 255 - (32 * livello)                          ' a ogni pixel scrivo il valore di profondità per avere il livello voluto

            Next

        Next
        ' ripristino vettore su immagine
        System.Runtime.InteropServices.Marshal.Copy(colorValues, 0, ptr, bytes)
        immagine.UnlockBits(bmpData) ' sblocco bit
        immagine.SetResolution(360, 360)
        immagine.Save("C:\Users\bernie\test8bpp_livello.bmp", Imaging.ImageFormat.Bmp)

    End Sub 


larghezza è la larghezza del file , larghezza1 è la larghezza del rettangolo.
altezza è l'altezza del file ,altezza1 è l'altezza del rettangolo.
Visto che i file che devo trattare sono abbastanza grandi ,si può ottimizzare qualcosa per rendere il processo più veloce?
Grazie


Ultima modifica effettuata da bernie 12/07/21 8:06
aaa
12/07/21 11:26
Carlo
Buono ma se devi scrivere rettangoli, è utile una sub che si incarica di farlo, ignorando i valori fuori scala.
Nell'esempio puoi definire un'immagine larga alta come vuoi, considera che poi si parte da 0, le coordinate di un'immagine larga 277 vanno da 0 a 276.

La routine che traccia rettangoli pieni accetta le coordinate dell'angolo superiore sinistro e l'angolo inferiore destro di un rettangolo, la larghezza dell'immagine (estratta da .stride che include anche i caratteri di escape), l'altezza, il vettore che contiene l'immagine, e il colore del rettangolo.

Per darti l'idea della velocità di esecuzione ho aggiunto Stopwatch che uso per mostrare sulla barra del titolo i millisecondi impiegati per scrivere i bytes nel vettore.

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim sw As New Stopwatch
        Dim larghezza As UInt16 = 5000 ' 0...4999, solo positivi
        Dim altezza As UInt16 = 2500 ' 0...2499, solo positivi
        Dim immagine As New Bitmap(larghezza, altezza, Imaging.PixelFormat.Format8bppIndexed)

        ' creo una palette con 256 livelli di grigio
        Dim palette As Imaging.ColorPalette = immagine.Palette
        For i = 0 To palette.Entries.Length - 1
            palette.Entries(i) = Color.FromArgb(i, i, i)
        Next i
        ' associo la palette creata alla bitmap 8bpp,
        ' i valori 0...255 ora corrispondono alla scala dal nero al bianco
        immagine.Palette = palette
        ' blocco i bit della bitmap per poter lavorare direttamente su un vettore di bytes
        Dim area As Rectangle = New Rectangle(0, 0, immagine.Width, immagine.Height)
        Dim bmpData As Imaging.BitmapData = immagine.LockBits(area, Imaging.ImageLockMode.ReadWrite, immagine.PixelFormat)

        ' puntatore all'indirizzo di memoria del primo byte.
        Dim ptr As IntPtr = bmpData.Scan0

        ' un vettore che conterrà tutti i bytes della bitmap.
        Dim bytes As Integer = Math.Abs(bmpData.Stride) * immagine.Height ' calcolo dei bytes necessari
        Dim colorValues(bytes - 1) As Byte

        sw.Restart() ' avvio il conteggio del tempo

        rettangolo(0, 0, 4999, 299, Math.Abs(bmpData.Stride), altezza, colorValues, 255) ' rettangolo bianco superiore
        rettangolo(0, 300, 299, 2499, Math.Abs(bmpData.Stride), altezza, colorValues, 100) ' rettanglo grigioscuro sinistro
        rettangolo(300, 300, 4999, 2499, Math.Abs(bmpData.Stride), altezza, colorValues, 200) ' rettangolo grigio

        Me.Text = "Scrittura Bytes in: " & sw.ElapsedMilliseconds & "ms." ' mostro il tempo impiegato
  
        ' ripristino vettore su immagine
        System.Runtime.InteropServices.Marshal.Copy(colorValues, 0, ptr, bytes)
        immagine.UnlockBits(bmpData) ' sblocco bit
        immagine.Save("prova8bpp.bmp", Imaging.ImageFormat.Bmp)
    End Sub

   Sub rettangolo(x1 As UInt16, y1 As UInt16, x2 As UInt16, y2 As UInt16, larghezza As UInt32, altezza As UInt16, vettore() As Byte, colore As Byte)
        ' limite valori
        If x1 >= larghezza Then x1 = larghezza - 1
        If x2 >= larghezza Then x2 = larghezza - 1
        If y1 >= altezza Then y1 = altezza - 1
        If y2 >= altezza Then y2 = altezza - 1

        For colonna = x1 To x2
            For riga = y1 To y2
                Dim ind As UInt32 = colonna + riga * larghezza 'calcolo l'indice
                vettore(ind) = colore
            Next
        Next

    End Sub

End Class


Il programma traccia 3 rettangoli con tre livelli di grigio (per identificarli), naturalmente i rettangoli tracciati dopo, se occupano le coordinate dei rettangoli tracciati prima, li sovrascrivono.
Considerato che scrivi Bytes direttamente in memoria, in VB.Net hai la massima velocità, anche se non paragonabile a GDI+.
Ultima modifica effettuata da Carlo 12/07/21 15:27
in programmazione tutto è permesso
14/07/21 7:04
bernie
Buon Giorno
Ho provato l'esempio di Carlo e ovviamente funziona. Poi ho iniziato a modificarlo in base alle mie esigenze. Fino qui tutto ok, quando inserisco le dimensioni del bmp che mi servono , iniziano le cose strane , mi crea il file , ma non è possibile aprire il file , nei dettagli non sono indicate le dimensioni , la profondità , niente . Per scriverlo , impiega circa 60 secondi.
Stavo cercando di processare un file da 4000X250000 pixel( forse devo arrivare anche oltre ) . Rispetto l'esempio di Carlo , ho cambiato alcune variabili da UInt16 a Long.
Forse devo passare a GDI+?
Grazie
aaa
14/07/21 11:54
Carlo
Duecentocinquantamila pixel?
Da Windows non ti aspettare di avere info su file BMP che superano i 130.000 pixel in altezza o larghezza.
Il file viene correttamente creato ma poi per maneggiarlo dovrai ricorrere a sistemi prioritari, il tuo sitema grafico che dice quando gli passi una BMP da 1GB?

Con GDI+ la velocità di tracciamento di un rettangolo pieno è circa 10 volte più veloce che riempire di bytes la memoria con i cicli per un'immagine a 8bpp e 5 volte più veloce che riempire per un'immagine a 4bpp.
Purtroppo con GDI+ un'immagine bitmap a pixel non indicizzati non può superare i 260.000 pixel, e se l'immagine è grande come le tue 1GB e più, poi la conversione da 16bpp a 4 bpp si rimangia il tempo guadagnato.

Sei sicuro di percorrere una strada giusta?

Per il momento ti propongo lo stesso sistema ma a 4bpp, avrai un raddoppio della velocità e un dimezzamento della grandezza del file.
Non ho messo il calcolo per distinguere due pixel adiacenti, che saranno trattati come fossero un unico pixel.

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim sw As New Stopwatch
        Dim larghezza As UInt32 = 4000 ' 0...3998, solo positivi, due a due
        Dim altezza As UInt32 = 130000 ' 0...129999, solo positivi

        Dim immagine As New Bitmap(larghezza, altezza, Imaging.PixelFormat.Format4bppIndexed)

        ' creo una palette con 16 livelli di grigio
        Dim palette As Imaging.ColorPalette = immagine.Palette
        For i = 0 To palette.Entries.Length - 1
            palette.Entries(i) = Color.FromArgb(i * 17, i * 17, i * 17)
        Next i
        ' associo la palette creata alla bitmap 4bpp,
        ' i valori 0...15 ora corrispondono alla scala dal nero al bianco
        immagine.Palette = palette
        ' blocco i bit della bitmap per poter lavorare direttamente su un vettore di bytes
        Dim area As Rectangle = New Rectangle(0, 0, immagine.Width, immagine.Height)
        Dim bmpData As Imaging.BitmapData = immagine.LockBits(area, Imaging.ImageLockMode.ReadWrite, immagine.PixelFormat)

        ' puntatore all'indirizzo di memoria del primo byte.
        Dim ptr As IntPtr = bmpData.Scan0

        ' un vettore che conterrà tutti i bytes della bitmap.
        Dim bytes As Integer = Math.Abs(bmpData.Stride) * immagine.Height ' calcolo dei bytes necessari
        Dim colorValues(bytes - 1) As Byte
        sw.Restart() ' avvio il conteggio del tempo

        rettangolo(0, 0, 4000, 299, Math.Abs(bmpData.Stride), altezza, colorValues, 15) ' rettangolo bianco superiore
        rettangolo(0, 300, 298, 130000, Math.Abs(bmpData.Stride), altezza, colorValues, 8) ' rettanglo grigioscuro sinistro
        rettangolo(300, 300, 4000, 130000, Math.Abs(bmpData.Stride), altezza, colorValues, 10) ' rettangolo grigio

        Me.Text = "Scrittura Bytes in: " & sw.ElapsedMilliseconds & "ms." ' mostro il tempo impiegato

        ' ripristino vettore su immagine
        System.Runtime.InteropServices.Marshal.Copy(colorValues, 0, ptr, bytes)
        immagine.UnlockBits(bmpData) ' sblocco bit
        immagine.Save("prova4bpp.bmp", Imaging.ImageFormat.Bmp)
    End Sub

    Sub rettangolo(x1 As UInt32, y1 As UInt32, x2 As UInt32, y2 As UInt32, larghezza As UInt32, altezza As UInt32, vettore() As Byte, colore As Byte)
        ' limite valori
        x1 = Math.Truncate(x1 / 2)
        x2 = Math.Truncate(x2 / 2)
        If x1 >= larghezza Then x1 = larghezza - 1
        If x2 >= larghezza Then x2 = larghezza - 1
        If y1 >= altezza Then y1 = altezza - 1
        If y2 >= altezza Then y2 = altezza - 1
        ' a 4bpp scrivo 2 pixel adiacenti alla volta
        colore = colore + colore * 16 ' valore per due pixel uguali
        For colonna As UInt32 = x1 To x2
            For riga As UInt32 = y1 To y2
                Dim ind As UInt32 = colonna + riga * larghezza  'calcolo l'indice
                vettore(ind) = colore
            Next
        Next
    End Sub

End Class


Ultima modifica effettuata da Carlo 15/07/21 10:15
in programmazione tutto è permesso