Problema nella scansione di tutti i pixel di un'immagine

giovedì 17 dicembre 2009 - 02.30

bgdev Profilo | Newbie

Ciao a tutti, vengo subito al dunque senza dilungarmi troppo.
Ho bisogno di scansionare tutti i pixel di una generica immagine e di ogni pixel devo ricavare le componenti RGB. Sono partito con la seguente funzione che svolge egregiamente il suo compito restituendo 3 array (R, G, B) con i vari valori, ma purtroppo è troppo lenta....
Public Function rgb(ByVal percorsoFile As String) Dim Immagine As Bitmap = Image.FromFile(percorsoFile) Dim Pixel As Color Dim RedValue As Int32 Dim GreenValue As Int32 Dim BlueValue As Int32 Dim R(256) As Int32 Dim G(256) As Int32 Dim B(256) As Int32 For X As Int32 = 0 To Immagine.Width - 1 For Y As Int32 = 0 To Immagine.Height - 1 Pixel = Immagine.GetPixel(X, Y) RedValue = Pixel.R GreenValue = Pixel.G BlueValue = Pixel.B R(RedValue) += 1 G(GreenValue) += 1 B(BlueValue) += 1 Next Next End Function
Allora mi sono documentato un po' è ho implementato il metodo Bitmap.LockBits per velocizzare la scansione. Il risultato è la seguente funzione:
Public Function rgb(ByVal percorsoFile As String) Dim R(256) As Int32 Dim G(256) As Int32 Dim B(256) As Int32 Dim immagine As New Bitmap(percorsoFile) Dim rect As New Rectangle(0, 0, immagine.Width, immagine.Height) Dim bmpData As BitmapData = immagine.LockBits(rect, _ Drawing.Imaging.ImageLockMode.ReadWrite, immagine.PixelFormat) Dim ptr As IntPtr = bmpData.Scan0 Dim bytes As Integer = immagine.Width * immagine.Height * 3 Dim rgbValues(bytes - 1) As Byte System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes) For counter As Integer = 0 To rgbValues.Length - 1 Step 3 R(rgbValues(counter)) += 1 G(rgbValues(counter + 1)) += 1 B(rgbValues(counter + 2)) += 1 Next immagine.UnlockBits(bmpData) End Function
All'inizio ero tutto soddisfatto perchè questa funzione è molto veloce e pensavo di aver risolto i miei problemi. Poi però mi sono accorto di un problema decisamente grave: i valori restituiti dalle due funzioni sono completamente diversi....Ho verificato che i valori restituiti dalla prima funzione sono quelli corretti, quindi spero che qualcuno riesca a darmi una mano a correggere la seconda.
Grazie

aiedail92 Profilo | Expert

Ciao

Il fatto è che nei data raw non vengono salvati in ordine (A) R G B, bensì B G R (A), quindi trovi il Blue in prima posizione, il Green sempre in seconda, Red in terza (e nel tuo caso basta, niente alpha), quindi la funzione andrebbe corretta da così

R(rgbValues(counter)) += 1 G(rgbValues(counter + 1)) += 1 B(rgbValues(counter + 2)) += 1

a così:

B(rgbValues(counter)) += 1 G(rgbValues(counter + 1)) += 1 R(rgbValues(counter + 2)) += 1

Comunque questo processo non è sempre corretto perché, per questioni di performance, alla fine di una "riga" di pixel, viene aggiunto del padding per allineare la memoria sui 4 byte, quindi se hai un'immagine con larghezza non multipla di 4, il risultato verrà sfasato.

In sostanza, una funzione di questo genere dovrebbe andare bene per i formati Format24bppRgb, Format32bppRgb e Format32bppArgb:

Public Function rgb(ByVal percorsoFile As String) As Int32()() Dim R(255) As Int32 Dim G(255) As Int32 Dim B(255) As Int32 Dim RedValue As Int32 Dim GreenValue As Int32 Dim BlueValue As Int32 Dim mul, num As Integer Dim immagine As New Bitmap(percorsoFile) Dim rect As New Rectangle(0, 0, immagine.Width, immagine.Height) Dim bmpData As BitmapData = immagine.LockBits(rect, _ ImageLockMode.ReadOnly, immagine.PixelFormat) Dim ptr As IntPtr = bmpData.Scan0 Dim bytes As Integer = bmpData.Stride * bmpData.Height Dim rgbValues(bytes - 1) As Byte System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes) Select Case bmpData.PixelFormat Case PixelFormat.Format24bppRgb mul = 3 Case PixelFormat.Format32bppRgb Case PixelFormat.Format32bppArgb mul = 4 Case Else Throw New NotSupportedException() End Select For y As Integer = 0 To bmpData.Height - 1 For x As Integer = 0 To bmpData.Width - 1 num = y * bmpData.Stride + mul * x BlueValue = rgbValues(num) GreenValue = rgbValues(num + 1) RedValue = rgbValues(num + 2) R(RedValue) += 1 G(GreenValue) += 1 B(BlueValue) += 1 Next Next immagine.UnlockBits(bmpData) Return New Int32()() {R, G, B} End Function


Tieni comunque presente che il formato dei pixel potrebbe anche essere diverso da questi, quindi dovresti scriverti delle funzioni personalizzate per la lettura di ogni possibile formato.

Luca
Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2023
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5