das Thema kam bereits schon öfters auf. Ich habe ein Bild das im Normalfall komplett schwarz ist (R=0, G=0, B=0), wobei die Werte eine gewissen Toleranz haben dürfen (maximal den Wert 22). Mein Bild hat immer einer Größe von 1280x720. Das Pixelformat kenne ich leider nicht. Kann man das herausfinden oder muss man das gar nicht spezifisch angeben?
Um zu überprüfen, ob das Bild wirklich schwarz ist möchte ich von jedem Pixel die RGB-Werte auslesen und mit dem Grenzwert vergleichen. Sobald ein Pixel außerhalb de Toleranz liegt wird die Methode beendet.
Bisher mache ich das ganze mit GetPixel. Das ganze ist bekanntermaßen sehr träge. Habe jetzt auch schon sämtliche Foren durchforscht und versucht mich mit dem Thema Lockbits auseinander zusetzten. Unteranderem auch das Thema GetPixel und SetPixel um Längen geschlagen. 800 mal schneller gelesen und die Klasse BitmapHelper verwendet. Mir ist aber nicht klar wie ich das jetzt für meine bisherige Code verwenden kann. Help me please ;)
private void Check_RGB_values_Click(object sender, EventArgs e)
{
int countPixel = 0;
Bitmap myBitmap = new Bitmap("C:\\blackscreen.bmp");
for (int x = 0; x < myBitmap.Width; x++) //1280
{
for (int y = 0; y < myBitmap.Height; y++) //720
{
countPixel++;
Color pixelColor = myBitmap.GetPixel(x, y);
string rotAnteil = pixelColor.R.ToString();
string gelbAnteil = pixelColor.G.ToString();
string blauAnteil = pixelColor.B.ToString();
if (pixelColor.R > 22)
{
MessageBox.Show("Red from pixel No." + countPixel + " is out of range. " + rotAnteil + "> 22");
return;
}
if (pixelColor.G > 22)
{
MessageBox.Show("Yellow from pixel No." + countPixel + " is out of range. " + gelbAnteil + "> 22");
return;
}
if (pixelColor.B > 22)
{
MessageBox.Show("Blue from pixel No." + countPixel + " is out of range. " + blauAnteil + "> 22");
return;
}
}
}
}
Für GetPixel und SetPixel hab ich mir angehängte Klasse erstellt -- in Anlehnung an andere Klassen die es dazu gibt (wie die von dir verlinkte).
Schau dir angehängt einmal an, dann siehst du wie das geht. Du kannst du Klasse auch direkt verwenden.
Anmerkung: ist schon ein alter Code -- IDisposable könnte auf jeden noch implementiert werden.
Die schnellste Variante wäre in dem Anwendungsfall, das Bild einfach in ein Byte-Array zu konvertieren, und dann die Werte im Array auf die gegebene Toleranz zu prüfen - dadurch kann man komplett auf Bitmap-Operationen verzichten.
Weeks of programming can save you hours of planning
Ein Schritt ist die Breite einer einzelnen Zeile von Pixeln (einer Scanzeile), aufgerundet auf eine 4-Byte-Begrenzung. Wenn der Schritt positiv ist, verläuft die Bitmap von oben nach unten. Wenn der Schritt negativ ist, verläuft die Bitmap von unten nach oben.
das Pixelformat konnte ich inzwischen ermitteln. Bekomme für GDI, Format24bppRgb und DontCare ein true. Mit der FastBitmap.cs bin ich leider nicht ganz zurecht gekommen wie das funktioniert die Pixelwerte abzufragen. Habe mich stattdessen für die Variante von MrSparkle entschieden und das so gelöst:
private void button1_Click(object sender, EventArgs e)
{
Bitmap blackscreen = new Bitmap("c:\\blackscreen.bmp");
BitmapToByteArray(blackscreen);
for (int x = 0; x < anzahlPixel; x++)
{
if (dataByte[x] > 22)
{
MessageBox.Show("Pixel "+x+" is not black");
return; // beende die Methode sobald der erste Pixel außerhalb der Toleranz liegt
}
}
}
Trotzdem würde mich auch noch die andere Lösung interessieren ohne das Bild in ein Array umzuwandeln.
Deine Lösung ist (meines Wissens) schon die zweitschnellste[1]. Ohne das Bitmap als Array zu behandeln, liefe wieder auf die GetPixel-Methode im ursprünglichen Posting hinaus, und die ist für so etwas eben nicht wirklich gut geeignet.
LaTino
[1] lässt sich unter Verwendung von unsafe code nochmal etwas beschleunigen.
"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)
Für GetPixel und SetPixel hab ich mir angehängte Klasse erstellt -- in Anlehnung an andere Klassen die es dazu gibt (wie die von dir verlinkte).
Schau dir angehängt einmal an, dann siehst du wie das geht. Du kannst du Klasse auch direkt verwenden.
Danke. Deinen Ansatz konnte ich jetzt auch nachvollziehen und habe beide Lösungen nun funktionsfähig.
Die schnellste Variante wäre in dem Anwendungsfall, das Bild einfach in ein Byte-Array zu konvertieren, und dann die Werte im Array auf die gegebene Toleranz zu prüfen - dadurch kann man komplett auf Bitmap-Operationen verzichten
Danke hat wunderbar geklappt und ist vor allem rasend schnell. Funktioniert das Ganze auch für Videos? Also ich möchte überprüfen, ob in dem Video ein Pixel schwarz geworden ist.
Davon abgesehen, dass der Stream unnötig ist, fehlt das sichere disposen, zB in Form eines using().
ReadAllByte alleine reicht vollkommen. Den Stream braucht man hier ja nicht.
Ich finde das irgendwie etwas umtändlich, wenn ich mein fertiges Video dann wieder in Frames zerstückeln muss. Das geht doch bestimmt einfacher alle RGB-Werte aller Pixel aller Frames auszulesen.
ein Problem nicht - aber den Unterschied sehe ich bei deiner Recherche nun auch nicht ;-)
Bei beiden Lösung wird das AVI-File letztendlich dekodiert und in Frames aufgeteilt - da du allerdings ohnehin schon AForge benutzt ist deine Recherche für dich sicher die beste Lösung.
aber wenn du doch sowieso das Video selbst erstellst, dann hast du doch jeden einzelnen Frame als Bitmap vorliegen. Und kannst dann wieder die Pixel überprüfen.
aber wenn du doch sowieso das Video selbst erstellst, dann hast du doch jeden einzelnen Frame als Bitmap vorliegen. Und kannst dann wieder die Pixel überprüfen.
Das ist richtig. Das wäre mir sogar am liebsten. Überlege nur noch wie ich das am besten umsetzte.
Meine Idee wäre dann im newFrameHandler nach jedem Frame.Clone das Bitmap zu speichern. Die einzelnen Bitmaps will ich aus Dokumentationsgründen speichern. Und anschließend das Bild sofort zu analysieren mit meiner Methode VideoToByteArray();
Also so:
void FinalVideo_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
if (stopVideo==false)
{
video = (Bitmap)eventArgs.Frame.Clone();
VideoToByteArray(video);
video.Save(pathXY);
FileWriter.WriteVideoFrame(video);
}
//....
}
Aber habe ich da nicht ein riesen Performanceverlust, wenn ich das alles in den Handler schreibe? Nehmen mir mal an die Methode VideoToByteArray() benötigt 2s dann wird ja auch maximal alle 2s ein neuer Frame gecloned, gespeichert und analysiert.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Elias1994 am .
Das ist ein nahezu Bilderbuch-Fall für den Producer Consumer Pattern.
- Ein Task liest die Bytes und schreibt sie in eine Queue
- Ein Task verarbeitet nun die Bytes
- Das Ergebnis geht an einen dritten Task
Prinzipielles Vorgehen sehr einfach erklärt unter TPL Pipelines
Und im Fall der Fälle lässt sich eine einzelne Queue von mehrere Tasks verarbeiten -> einfache Skalierung.
Die schnellste Variante wäre in dem Anwendungsfall, das Bild einfach in ein Byte-Array zu konvertieren, und dann die Werte im Array auf die gegebene Toleranz zu prüfen - dadurch kann man komplett auf Bitmap-Operationen verzichten.
Liege ich der Annahme richtig, dass die Bytes nach folgendem Prinzip in das Array geschrieben werden:
- Die RGB Werte werden beginnend links oben am Bild Zeile für Zeile bis
einschließlich Bildende (rechts unten) ( also so wie man ein Buch liest ;) )
- ´Bytearray[0] =B-Wert des ersten Pixels
- ´Bytearray[1] =G-Wert des ersten Pixels
- ´Bytearray[2] =R-Wert des ersten Pixels
- ´Bytearray[3] =B-Wert des ersten Pixels
- ´Bytearray[4] =G-Wert des ersten Pixels
- ´Bytearray[5] =R-Wert des ersten Pixels
Zumindest hat dies meine Analyse des Bytearrays ergeben. Hat mich etwas verwundert, da ich davon ausgegangen war, dass erst die R-Wert des ersten Pixels im Array steht und nicht der B-Wert.
Super danke. Stimmt daran sieht man sehr gut. Kann man das irgendwo nachlesen, wann welches Pixelformat benutzt wird. Ich weiß, dass ich das Pixelformat abfragen kann. Mich würde, dass nur interessieren, was wo seine Anwendung findet.
Und was mich noch interessieren würde:
Wenn ein Bild ein bestimmtes Pixelformat hat, lässt sich das Bild dann in jedes beliebige andere Pixelformat umwandeln?
Wenn ein Bild ein bestimmtes Pixelformat hat, lässt sich das Bild dann in jedes beliebige andere Pixelformat umwandeln?
du kannst das Format einfach beim Locken der Bitmap angeben. Du kannst sogar gleichzeitig die Konvertierung in einen von dir verwendeten Speicher vornehmen:
Hier ist das Ausgabeformat 24bpp BGR. Du kannst natürlich die Struct für den einzelnen Pixel und das Format entsprechend anpassen (Stride nicht vergessen).
Ob die Konvertieren wirklich alle Formate beliebig konvertieren kann, kann ich dir nicht sagen.