5 Stimmen

Ermittlung der prozentualen Verwendung jeder Farbe in einem Bild

Ich habe dieses Programm, das funktioniert, aber es ist so verdammt langsam bei jpeg-Bildern und braucht auch einige Änderungen.

Ich muss die einzelnen Farben in einem Bild (mit einer Toleranz von +/- 1 für RGB) und den prozentualen Anteil dieser Farbe am Bild kennen.

Wenn ein Bild also schwarz-weiß ist, würde es etwa so lauten Weiß : 74% Schwarz : 26%

Der Code unten funktioniert wie gesagt, aber ich muss auch ein Toleranzsystem hinzufügen, und ich habe keine Ahnung, wie ich das machen würde.

private Dictionary<string, string> getPixelData(Bitmap image)
{
    Dictionary<string, string> pixelData = new Dictionary<string, string>();
    //int col, row;
    //int r, g, b;
    Color pixel;

    double offset = 0.000001;
    int hmm = (image.Height * image.Width);
    double current = 0;
    offset = 100 / double.Parse(hmm.ToString());// 0.01;// 100 / (image.Height * image.Width) * 10000;

    try
    {
        for (int i = 0; i < image.Height; i++)
        {
            for (int j = 0; j < image.Width; j++)
            {
                current = current + offset;
                pixel = image.GetPixel(i, j);                        
                pixelData.Add(i + "," + j, (pixel.R.ToString() + " " + pixel.G.ToString() + " " + pixel.B.ToString()));
                pBarprocess.Value = int.Parse(Math.Floor(current).ToString());
                pBarprocess.Update();
                Application.DoEvents();
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("Unable to parse image " + ex);
    }

    return pixelData;
}

Und die andere Funktion

private void btnProcess_Click(object sender, EventArgs e)
{
    pBarprocess.Value = 0;
    pBarprocess.Enabled = false;
    Bitmap foo = Bitmap.FromFile(@txtFileName.Text) as Bitmap;
    Dictionary<string, string> pixelData = new Dictionary<string, string>();

    lblProcess.Text = "Processing pixel map";
    pixelData = getPixelData(foo);

    lblProcess.Text = "Calculating Density";
    lblProcess.Update();

    var distinctList = pixelData.Values.Distinct().ToList();

    Console.WriteLine("DL = " + distinctList.Count);
    double offset = 100 / double.Parse(distinctList.Count.ToString());
    double current = 0;

    foreach (var value in distinctList)
    {
        IEnumerable<string> query = pixelData.Values.Where(fruit => fruit == value);
        double perc = (double.Parse(query.Count().ToString()) / double.Parse(pixelData.Count.ToString())) * 100;
        Console.WriteLine(value + " = " + query.Count() + "(" + perc + "%)");
        txtAnalysis.Text = "Colour " + value + " : " + query.Count() + " (" + perc.ToString() + "%)\r\n" + txtAnalysis.Text;
        txtAnalysis.Update();
        pBarprocess.Value = int.Parse(Math.Floor(current).ToString());
        pBarprocess.Update();
        Application.DoEvents();
    }

    lblProcess.Text = "Finished.";
    pBarprocess.Value = 0;
    pBarprocess.Enabled = false;
}

11voto

Andreas Punkte 6325

GetPixel ist nicht wirklich ein schneller Weg, um auf Bilddaten zuzugreifen. Verwenden Sie die LockBits Methode.

EDITAR:

Nun, du machst eine Menge Dinge mit Saiten. Der Aufbau des PixelData-Dictionarys auf diese Weise ist ziemlich nutzlos, warum verarbeiten Sie nicht gleich die einzelnen Farben? Farbe ist eine unveränderliche Struktur, also ist das bereits ein guter Schlüssel für unser Wörterbuch.

Dictionary<Color, int> frequency = new Dictionary<Color, int>();
for (int i = 0; i < image.Height; i++) {
  for (int j = 0; j < image.Width; j++) {
    pixel = image.GetPixel(i, j);
    if (frequency.ContainsKey(pixel)) frequency[pixel]++;
    else frequency.Add(pixel, 1);
  }
}

// and finally
int totalPixels = image.Width * image.Height;
foreach (var kvp in frequency) {
  Console.WriteLine("Color (R={0},G={1},B={2}): {3}", kvp.Key.R, kvp.Key.G, kvp.Key.B, kvp.Value / (double)totalPixels);
}

Das sollte genügen, es sei denn, Sie wollen es noch schneller machen und LockBits anstelle von GetPixel verwenden.

Einige andere Beobachtungen:

int hmm = (image.Height * image.Width);
double offset = 100 / double.Parse(hmm.ToString());

Sie verwenden eine sehr seltsame und langsame Art und Weise des Castings von int zu double. Sie können einfach schreiben double offset = 100 / (double)hmm; und es ist das gleiche (Sie könnten auch 100.0 und nicht 100 schreiben und der Compiler wird ein Double für Sie erstellen, so dass Sie nicht casten müssen hmm).

Das hat mich zum Lachen gebracht:

IEnumerable<string> query = pixelData.Values.Where(fruit => fruit == value);

Warum Obst!? Es scheint, als hätten Sie das von irgendwoher kopiert.

8voto

Lilith River Punkte 16254

Es scheint, dass dies Teil eines größeren Ziels der Bildverarbeitung ist. Die Aforge-Rahmen ist die erste Wahl für die Bildanalyse und -verarbeitung in .NET, und es ist extrem schnell. Es ist wahrscheinlich, dass der Code, den Sie benötigen, bereits vorhanden ist.

Sie erwähnten ein Toleranzsystem - für mich klingt das, als bräuchten Sie Quantisierung - Farbrundung .

Sobald Sie eine quantisierte Bitmap haben, können Sie ein Array mit einer Länge erstellen, die der Palettengröße entspricht, die Bitmap mit LockBits versehen und den Farbindex für jedes Pixel als Array-Index für jedes Pixel verwenden, um Nutzungsstatistiken zu sammeln.

Könnten Sie mehr Informationen über Ihre Ziele für diesen Code mitteilen? Was soll er genau tun?

-1voto

Ahmet Dereci Punkte 1

Meine Methode zur Berechnung des prozentualen Farbanteils eines Bildes ist wie folgt, auch auf diese Weise können wir

einen beliebigen Prozentsatz von Pixeln für eine beliebige Farbe berechnen.

1. eine Software namens "ImageJ" an dieser Stelle zu verwenden, sie ist kostenlos.

http://rsb.info.nih.gov/ij/download.html

2. ein Bild mit diesem Werkzeug öffnen

  1. Gehen Sie zum Menü Analysieren und wählen Sie Histogramm, es öffnet sich das Histogramm-Fenster

4. im Histogramm-Fenster unten links auf die Schaltfläche "Liste" klicken, dadurch wird das Listenfenster geöffnet

5. im Listenfenster wählen Sie "Speichern unter", dies speichert die Anzahl der Pixel pro Farbe zwischen 0-256

6. für die Flächenmessung die Pixelgröße aus der Auflösung berechnen und mit der Anzahl der Pixel multiplizieren

Pixel. Verwenden Sie Excel für andere numerische Analysen, insbesondere für Pivot-Tabellen.

Hier sind die ImageJ-Software, die Excel-Datei, die ich verwendet habe, und ein Bildschirmfoto.

https://www.mediafire.com/?htwk83rwgio4zds

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X