Die Community zu .NET und Classic VB.
Menü

VB.NET-Tipp 0092: Zahl der Farben einer Bitmap

 von 

Beschreibung

Das folgende Beispiel zeigt, wie die Anzahl der unterschiedlichen Farben in einem Bild ermittelt werden können.
Die Vorgehensweise, ein großes Array (rund 2MB) anzulegen, jeden Farbwert des Bildes als Index in diesem Array zu verwenden und dort ein Bit zu setzen, hat sich als die schnellste Methode erwiesen. In einem direkten Vergleich dieser VB.NET-Implementierung gegen eine Implementierung in Assembler des selben Codes war ASM nur etwa um den Faktor 2 schneller.

Schwierigkeitsgrad:

Schwierigkeitsgrad 2

Framework-Version(en):

.NET Framework 2.0, .NET Framework 3.0, .NET Framework 3.5

.NET-Version(en):

Visual Basic 2005, Visual Basic 2008

Download:

Download des Beispielprojektes [259,78 KB]

' Dieser Quellcode stammt von http://www.activevb.de
' und kann frei verwendet werden. Für eventuelle Schäden
' wird nicht gehaftet.

' Um Fehler oder Fragen zu klären, nutzen Sie bitte unser Forum.
' Ansonsten viel Spaß und Erfolg mit diesem Source!

' Projektversion:   Visual Studio 2008
' Option Strict:    An
' Option Explicit:  An
' Option Infer:     An
'
' Referenzen: 
'  - System
'  - System.Data
'  - System.Deployment
'  - System.Drawing
'  - System.Windows.Forms
'  - System.Xml
'  - System.Core
'  - System.Xml.Linq
'  - System.Data.DataSetExtensions
'
' Imports: 
'  - System
'  - System.Collections
'  - System.Collections.Generic
'  - System.Data
'  - System.Drawing
'  - System.Diagnostics
'  - System.Windows.Forms
'  - System.Linq
'  - System.Xml.Linq
'

' ##############################################################################
' ################################# Form1.vb ###################################
' ##############################################################################
Imports System.Runtime.InteropServices

Public Class Form1
    Private Sub Form1_Shown(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles Me.Shown

        Dim o As New IO.DirectoryInfo( _
            System.Windows.Forms.Application.StartupPath)
        LoadNewPicture(o.Parent.Parent.FullName & "\P8301141.JPG")
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles Button1.Click

        If OpenFileDialog1.ShowDialog() = Windows.Forms.DialogResult.OK Then
            LoadNewPicture(OpenFileDialog1.FileName)
        End If
    End Sub

    Private Sub LoadNewPicture(ByVal FullName As String)
        Dim bmp As New Bitmap(FullName)

        PictureBox1.Image = bmp
        Dim c As UInt32 = CountColors(bmp)
        MessageBox.Show("Das Bild enthält " & c & " unterschiedliche Farben.")
    End Sub

    Private Function CountColors(ByVal InBitmap As Bitmap) As UInt32
        ' Um komfortabler durch die Bilddaten iterieren zu können, wird das Bild
        ' zuerst in 32Bit Farbtiefe konvertiert:
        Dim dwPixels(InBitmap.Height * InBitmap.Width - 1) As UInt32
        Dim hPixels As GCHandle = GCHandle.Alloc(dwPixels, GCHandleType.Pinned)
        Dim bmp32Bpp As New Bitmap(InBitmap.Width, _
            InBitmap.Height, InBitmap.Width * 4, _
            Imaging.PixelFormat.Format32bppRgb, hPixels.AddrOfPinnedObject)

        Using gr As Graphics = Graphics.FromImage(bmp32Bpp)
            gr.DrawImageUnscaledAndClipped(InBitmap, New Rectangle(0, 0, _
                bmp32Bpp.Width, bmp32Bpp.Height))
        End Using

        ' Jetzt zählen:
        Dim dwTable(CInt(2 ^ 19) - 1) As Int32
        Dim index As Int32
        Dim bit As Int32
        Dim cDifferentColors As UInt32
        For i = 0 To dwPixels.Length - 1
            ' das höchstwertige Byte ist unbenutzt und muss ausmaskiert werden:
            index = CInt(dwPixels(i) And &HFFFFFF)
            bit = 1 << (index And &H1F)
            index >>= 5

            If (dwTable(index) And bit) = 0 Then
                cDifferentColors += 1UI
                dwTable(index) = dwTable(index) Or bit
            End If
        Next

        ' GC Handle freigeben:
        hPixels.Free()

        Return cDifferentColors
    End Function
End Class

Ihre Meinung  

Falls Sie Fragen zu diesem Artikel haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.

Archivierte Nutzerkommentare 

Klicken Sie diesen Text an, wenn Sie die 1 archivierten Kommentare ansehen möchten.
Diese stammen noch von der Zeit, als es noch keine direkte Forenunterstützung für Fragen und Kommentare zu einzelnen Artikeln gab.
Aus Gründen der Vollständigkeit können Sie sich die ausgeblendeten Kommentare zu diesem Artikel aber gerne weiterhin ansehen.

Kommentar von Richard Bauer am 16.08.2011 um 15:31

Hallo -
ich bin wirklich begeistert von dieser schnellen funktion ...

und habe (vergeblich) versucht den "schlüssel" bei 32bit besser zu verstehen ...

oder anders gesagt; mit bitshiften noch neuling :-)

besteht die möglichkeit mir ein bisserl auf die sprünge zu helfen:

For i = 0 To dwPixels.Length - 1
' das höchstwertige Byte ist unbenutzt und muss ausmaskiert werden:
index = CInt(dwPixels(i) And &HFFFFFF)
' dwPixels(i) enthält doch die FARBE/nummer !?
' ist diese vom ARGB-format und wie könnte ich diese aus
' z.B: dwPixels(i) = 4294901760 zuordnen ?
'
bit = 1 << (index And &H1F)
' wird hier die ERSTE stelle (die 4) weggeshiftet
index >>= 5
' und hier um "A(rgb)" nach rechts ?
Dim test As Integer = CInt(dwPixels(i) >> 1)

If (dwTable(index) And bit) = 0 Then
cDifferentColors += 1UI
dwTable(index) = dwTable(index) Or bit
End If
Next

währe echt super mir da den "lehrlingseffekt" zukommen zu lassen *wedel*

danke