Algorithmus zum Reduzieren einer Bitmap-Maske auf eine Liste von Rechtecken?


7

Bevor ich einen Nachmittag damit verbringe, dies selbst zu schreiben, dachte ich, ich würde fragen, ob bereits eine Implementierung verfügbar ist - auch nur als Referenz. Das erste Bild ist ein Beispiel für eine Bitmap-Maske, die ich in eine Liste von Rechtecken umwandeln möchte. Ein schlechter Algorithmus würde jedes festgelegte Pixel als 1x1-Rechteck zurückgeben. Ein guter Algorithmus würde wie das zweite Bild aussehen, in dem die Koordinaten der orangefarbenen und roten Rechtecke zurückgegeben werden. Die Tatsache, dass sich die Rechtecke überlappen, spielt keine Rolle, nur dass nur zwei zurückgegeben werden.

Zusammenfassend wäre das ideale Ergebnis diese beiden Rechtecke (x, y, w, h): [ { 3, 1, 2, 6 }, { 1, 3, 6, 2 } ]

Antworten:


6

Ich kenne keinen Algorithmus für dieses spezielle Problem, aber hier ist eine Idee, wie Sie dies angehen können:

  1. Lass Xund Ysei0
  2. Suchen und erhöhen Sie (X, Y)Ihre Bitmap und besuchen Sie jedes Pixel von links nach rechts, von oben nach unten, bis Sie auf ein nicht besuchtes Vordergrundpixel treffen.
  3. Suchen Sie von (X, Y)rechts, bis Sie ein Hintergrundpixel oder ein bereits besuchtes Pixel treffen. Behalten Sie die Anzahl der gefundenen Pixel bei WIDTH. Set HEIGHTzu 1.
  4. Starten Sie eine weitere Suche unter ( X, Y + HEIGHT)und suchen Sie nach WIDTHPixeln. Wenn alle diese Pixel zum Vordergrund gehören, erhöhen Sie sie HEIGHTum 1.
  5. Schritt wiederholen 3, bis Sie bei der Suche auf ein Hintergrundpixel treffen. Dann fügen Sie das Rechteck (gebildet durch X, Yund WIDTH, HEIGHT) auf der Liste der gefundenen Rechtecke und markieren Sie alle Pixel als besucht umschließt.
  6. Zuwachs X durch WIDTHund setzen Sie Ihre Suche (gehen Sie zu Schritt 2 ). Ihre Suche endet, wenn Sie das untere rechte Pixel Ihrer Bitmap erreicht haben.

Der Algorithmus würde 3 Rechtecke für Ihr spezifisches Beispiel erzeugen. Um ein Ergebnis mit nur 2 Rechtecken zu erhalten, müssten Sie den Schritt ändern 3 so , dass er nur bei einem Hintergrundpixel stoppt und bereits besuchte Pixel ignoriert. Dies würde jedoch möglicherweise zu vielen überlappenden Rechtecken führen. So könnte Schritt 3 weiter optimiert werden, indem nicht besuchte und besuchte Vordergrundpixel verfolgt werden. Wenn das letzte Pixel (vor dem Hintergrundpixel) ein besuchtes Pixel ist, verwenden Sie das letzte nicht besuchte Vordergrundpixel, um das zu erhalten WIDTH.

Update: Ich habe den von mir vorgeschlagenen Algorithmus implementiert. Hier ist eine interaktive Demo, in der Sie es selbst ausprobieren können (klicken Sie auf "Flash-Vollbild abspielen", wenn das Display zu klein ist).

Steuerelemente: Klicken und ziehen Sie in den schwarzen Bereich, um Pixel zu zeichnen. Halten Sie Shiftund zeichnen Sie, um zu löschen. Doppelklicken Sie, um die Leinwand zu löschen.

Hier ist die vollständige Quelle der Flash-Demo .


Diese Frage wurde kürzlich in meiner SO-Geschichte gestoßen, aber ich kann mich ehrlich gesagt nicht erinnern, diese Frage überhaupt gestellt zu haben, geschweige denn, warum ich sie überhaupt brauchte. :) Das heißt, Ihr Algorithmus und Ihre Quelle scheinen genau das Richtige für ... was auch immer ich wollte. Vielen Dank.
Moswald

3

Dieses Problem ist nicht genau spezifiziert. Da Sie ein einzelnes, vereinfachtes Beispiel angegeben haben, haben Sie einige der wichtigen logischen Möglichkeiten und Eckfälle hier übersehen (oder Sie haben sie nur nicht explizit ausgeschlossen). Da Sie beispielsweise die vorgeschlagene Anwendung hierfür nicht erwähnen, haben Sie nicht angegeben, was wichtiger ist: Aufteilen der gesamten Karte in exklusive Rechtecke mit einer ebenso großen Fläche (das Packing-Problem, das beliebig ist Anzahl der Lösungen) oder einfach kantenangepasste Rechtecke in x und y erhalten, wie Sie oben zu implizieren scheinen. Letzteres nehme ich an.

Stellen Sie sich den Fall vor, in dem Sie jede der 4 Kacheln ausfüllen, die sich unmittelbar in den Ecken der oben gezeichneten Bitmaske in Pluszeichenform befinden ([2, 2] ist die oberste). Was sind nun die optimalen Rechtecke? Wenn Sie nur Spalten betrachten, würden Sie schmalere, längere Bereiche mit schmaleren Seiten bevorzugen oder das große, breitere Quadrat, das sich jetzt in der Mitte des Bildes befindet, mit gedrungenen Quadraten oben und unten?

Unter der Annahme möglichst langer kantenangepasster Rechtecke ist dies eine Lösung für den ersten Versuch:

  • Teilen Sie die Bitmaske in Spalten auf. Jede Spalte beginnt bei einem Wert y0 und endet bei einem anderen Wert y1. Speichern Sie jede Spalte als (y0, y1) Tupel in einer Liste.
  • Spalten: Bewerten Sie die Liste. Finden Sie heraus, wo benachbarte Tupel das gleiche y0 UND y1 zwischen sich haben (z. B. Spalte A beginnt bei y0 = 1 und endet bei y1 = 4; Spalte B auch). Solange jede aufeinanderfolgende Spalte diese hat, erhöhen Sie die Breite eines Rechtecks, das Sie erstellt haben, um diese darzustellen, um eins. Beenden Sie dieses Rechteck, speichern Sie es und erstellen Sie ein neues für alle nachfolgenden Spaltengruppen.

Wiederholen Sie die beiden obigen Schritte für Zeilen genau wie für Spalten (verwenden Sie stattdessen x0 und x1 und natürlich eine separate Liste).

Sie sollten jetzt einige Listen mit sauberen Rechtecken haben. Sie sind jedoch möglicherweise nicht optimal für Ihre Anwendung. In diesem Fall benötigen Sie ein Heuristiksystem, das eine effizientere Verpackung ermöglicht. Weitere Informationen finden Sie im Verpackungsproblem .

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.