EINFÜHRUNG |
Wir fassen ein Bild als ein ebene, rechteckförmige Fläche auf, auf der sich farbige Formen befinden. In der Druck- und Computertechnik beschreibt man ein Bild durch eine gitterartige Anordnung von farbigen Bildpunkten, auch Pixels genannt. Die Anzahl Bildpunkte pro Flächeneinheit wird Bildauflösung genannt und oft in dots per inch (dpi) angegeben. Um ein Bild auf dem Computer zu speichern und zu verarbeiten, muss die Farbe als Zahl definiert werden. Es gibt dazu mehrere Möglichkeiten, die man Farbmetriken oder Farbmodelle nennt. Eines der bekanntesten Modelle ist das RGB-Farbmodell, wo die Intensitäten der drei Farbkomponenten für Rot, Grün und Blau als Zahlen zwischen 0 (dunkel) und 255 (hell) angegeben werden [mehr...
Dies entspricht auch dem Farbwahrnehmung des menschlichen Auges, Zusammengefasst: Ein Computerbild besteht aus einer rechteckigen Anordnung von Pixels, die als Farben codiert sind. Oft spricht man von einer Bitmap. |
FARBMISCHUNG IM RGB-MODELL |
from gpanel import * xRed = 200 yRed = 200 xGreen = 300 yGreen = 200 xBlue = 250 yBlue = 300 makeGPanel(Size(501, 501)) window(0, 501, 501, 0) # y axis downwards bm = GBitmap(500, 500) for x in range(500): for y in range(500): red = green = blue = 0 if (x - xRed) * (x - xRed) + (y - yRed) * (y - yRed) < 16000: red = 255 if (x - xGreen) * (x - xGreen) + (y - yGreen) * (y - yGreen) < 16000: green = 255 if (x - xBlue) * (x - xBlue) + (y - yBlue) * (y - yBlue) < 16000: blue = 255 bm.setPixelColor(x, y, makeColor(red, green, blue)) image(bm, 0, 500) |
MEMO |
Farben werden mit ihrem Rot-, Grün- und Blauanteil definiert. makeColor(red, green, blue) setzt diese Farbanteile zu einer Farbe (einem Farbobjekt) zusammen. Für Bilder wird meist ein Integer-Koordinatensystem mit dem Ursprung in der oberen linke Ecke und nach unten zeigender positiver y-Achse verwendet [mehr... Da das GPanel Float-Koordinaten verwendet, können sich Rundungsfehler ergeben]. |
GRAUSTUFENBILD SELBST GEMACHT |
Du hast dich vielleicht manchmal gefragt, wie deine Bildverarbeitungs-Software (wie Photoshop, o.ä.) funktioniert. Hier lernst du einige einfache Verfahren kennen. Dein Programm macht aus einem Farbbild ein Graustufenbild, indem du den Mittelwert des Rot-, Grün- und Blauanteils bestimmst und diesen zur Definition des Grauwerts verwendest. from gpanel import * size = 300 makeGPanel(Size(2 * size, size)) window(0, 2 * size, size, 0) # y axis downwards img = getImage("sprites/colorfrog.png") w = img.getWidth() h = img.getHeight() image(img, 0, size) for x in range(w): for y in range(h): color = img.getPixelColor(x, y) red = color.red green = color.green blue = color.blue intensity = (red + green + blue) / 3 gray = makeColor(intensity, intensity, intensity) img.setPixelColor(x, y, gray) image(img, size, size) |
MEMO |
Aus einem Farbobjekt kannst du mit den Methoden color.red, color.green und color.bluedie Farbwerte als Integer bestimmen. Der Hintergrund muss weiss und nicht etwa transparent sein. Willst du die Transparenz berücksichtigen, so kannst du mit alpha = getAlpha() den Transparenzwert bestimmen und diesen in makeColor(red, green, blue, alpha) verwenden. |
WIEDERVERWENDBARKEIT |
Bei vielen Bildbearbeitungen muss der Benutzer einen Teilbereich des Bildes auswählen. Dazu zieht er mit der Maus ein temporäres Rechteck ("Gummiband-Rechteck"). Beim Loslassen der Maustaste wird der rechteckige Bereich definitiv auswählt. Es ist ratsam, zuerst dieses Teilproblem zu lösen, da dieser Code später in vielen Bildbearbeitungsprogrammen wieder verwendet werden kann. Wiederverwendbarkeit ist ein Gütezeichen für alle Software-Entwicklungen. Wie du früher gesehen hast, kann man das Zeichnen von Gummiband-Linien als Animation auffassen. Dabei muss aber bei jeder Bewegung das ganze Bild immer wieder neu aufgebaut werden. Ein eleganter Trick, um dies zu vermeiden, ist der XOR-Zeichnungsmodus. In diesem Modus wird eine neue Figur mit der darunter liegenden so verschmolzen, dass durch erneutes Darüberzeichnen die Figur wieder gelöscht wird, ohne dass sich das darunterliegende Bild verändert. Der Nachteil besteht darin, dass beim Zeichnen der Figur die Farben verändert werden. Dies kann man aber im Zusammenhang mit Gummiband-Rechtecken meist in Kauf nehmen.
from gpanel import * size = 300 def onMousePressed(e): global x1, y1 global x2, y2 setColor("blue") setXORMode(Color.white) # set XOR paint mode x1 = x2 = e.getX() y1 = y2 = e.getY() def onMouseDragged(e): global x2, y2 rectangle(x1, y1, x2, y2) # erase old x2 = e.getX() y2 = e.getY() rectangle(x1, y1, x2, y2) # draw new def onMouseReleased(e): rectangle(x1, y1, x2, y2) # erase old setPaintMode() # establish normal paint mode ulx = min(x1, x2) lrx = max(x1, x2) uly = min(y1, y2) lry = max(y1, y2) doIt(ulx, uly, lrx, lry) def doIt(ulx, uly, lrx, lry): print("ulx = ", ulx, "uly = ", uly) print("lrx = ", lrx, "lry = ", lry) x1 = y1 = 0 x2 = y2 = 0 makeGPanel(Size(size, size), mousePressed = onMousePressed, mouseDragged = onMouseDragged, mouseReleased = onMouseReleased) window(0, size, size, 0) # y axis downwards img = getImage("sprites/colorfrog.png") image(img, 0, size) |
MEMO |
Du holst dir die Bitmap für eine Bild, das du auf dem Computer gespeichert hast mit getImage(), wobei du den vollständig qualifizierten Dateinamen oder auch nur einen Teil des Pfades relativ zum Verzeichnis, in dem sich dein Programm befindet, angeben musst. Für Bilder, die sich in der Distribution befinden, verwendest du den Verzeichnisnamen sprites. Beim Press-Event setzst du das System in den XOR-Modus, damit du beim Drag-Event mit zweimaligem Zeichnen zuerst das alte Rechteck löschen und dann das neue zeichnen kannst. Dazu musst du die Eckpunkte in den globalen Werten x1, y1, x2, y2 abspeichern. Wenn du beim Release-Event das Gummiband-Rechteck nochmals zeichnest, bevor du in den Paint-Mode wechselst, so wird das Rechteck verschwinden. Wenn du aber zuerst in den Paint-Mode wechselt, bleibt es erhalten. Das Programm funktioniert unabhängig davon, wie du das Rechteck ziehst. Es liefert immer die richtigen Werte für ulx,uly und lrx, lry (immer ulx < lrx, uly < lry). Beachte, dass du die Mauskoordinaten nicht auf Window-Koordinaten umrechnen musst, da beide gleich gross sind, wenn du bei der Fenstergrösse mit Size() und dem Koordinatensystem mit window() dieselbe Werte verwendest. Du kriegst auch Drag-Events, wenn du die Maus aus dem Fenster ziehst. Du musst aufpassen, was du mit solchen Koordinaten machst, sonst kann das Programm unerwartet crashen. |
ROTAUGEN-EFFEKT |
from gpanel import * size = 300 def onMousePressed(e): global x1, y1 global x2, y2 setColor("blue") setXORMode("white") x1 = x2 = e.getX() y1 = y2 = e.getY() def onMouseDragged(e): global x2, y2 rectangle(x1, y1, x2, y2) # erase old x2 = e.getX() y2 = e.getY() rectangle(x1, y1, x2, y2) # draw new def onMouseReleased(e): rectangle(x1, y1, x2, y2) # erase old setPaintMode() ulx = min(x1, x2) lrx = max(x1, x2) uly = min(y1, y2) lry = max(y1, y2) doIt(ulx, uly, lrx, lry) def doIt(ulx, uly, lrx, lry): for x in range(ulx, lrx): for y in range(uly, lry): col = img.getPixelColor(x, y) red = col.red green = col.green blue = col. blue col1 = makeColor(3 * red / 4, green, blue) img.setPixelColor(x, y, col1) image(img, 0, size) x1 = y1 = 0 x2 = y2 = 0 makeGPanel(Size(size, size), mousePressed = onMousePressed, mouseDragged = onMouseDragged, mouseReleased = onMouseReleased) window(0, size, size, 0) # y axis downwards img = getImage("sprites/colorfrog.png") image(img, 0, size) |
MEMO |
Der Code zur Bildbearbeitung wird in die Funktion doIt() eingeklinkt. Alles andere übernimmst du unverändert vom vorhergehenden Programm. Du kannst den Abschwächungsgrad für die rote Farbe anpassen. Hier wird die Rotintensität auf 75% hinuntergesetzt. Beachte den doppelten Bruchstrich, der eine Integer-Division durchführt (der Divisionsrest wird vernachlässigt). Das Resultat ist wieder ein Integer, wie es auch sein muss. Das Programm zeigt noch einige Fehlerhalten, die du leicht beheben kannst. Zum einen verfärbt es auch nicht rote Gebiete, zum anderen schmiert es ab, wenn du das Gummiband-Rechteck aus dem Fenster ziehst. Es wäre natürlich nett, wenn das Programm die roten Augen selbst finden würde. Dazu müsste es aber das Bild analysieren und Bildinhalte automatisch erkennen, was ein besonders schwieriges Problem der Informatik ist [mehr... Bilderkennung ist ein Teilgebiet der hochaktuellen Mustererkennung]. |
BILDER AUSSCHNEIDEN UND ABSPEICHERN |
from gpanel import * size = 300 def onMousePressed(x, y): global x1, y1 global x2, y2 setColor("blue") setXORMode("white") x1 = x2 = int(x) y1 = y2 = int(y) def onMouseDragged(x, y): global x2, y2 rectangle(x1, y1, x2, y2) # erase old x2 = int(x) y2 = int(y) rectangle(x1, y1, x2, y2) # draw new def onMouseReleased(x, y): rectangle(x1, y1, x2, y2) # erase old setPaintMode() ulx = min(x1, x2) lrx = max(x1, x2) uly = min(y1, y2) lry = max(y1, y2) doIt(ulx, uly, lrx, lry) def doIt(ulx, uly, lrx, lry): width = lrx - ulx height = lry - uly if ulx < 0 or uly < 0 or lrx > size or lry > size: return if width < 20 or height < 20: return cropped = GBitmap.crop(img, ulx, uly, lrx, lry) p = GPanel(Size(width, height)) # another GPanel p.window(0, width, 0, height) p.image(cropped, 0, 0) rc = save(cropped, "mypict.jpg", "jpg") if rc: p.title("Saving OK") else: p.title("Saving Failed") x1 = y1 = 0 x2 = y2 = 0 makeGPanel(Size(size, size), mousePressed = onMousePressed, mouseDragged = onMouseDragged, mouseReleased = onMouseReleased) window(0, size, size, 0) # y axis downwards img = getImage("sprites/colorfrog.png") image(img, 0, size) |
MEMO |
Du kannst bei Bedarf mehrere GPanel-Fenster anzeigen, indem du GPanel-Objekte erzeugst. Zum Zeichnen verwendest du die Grafikbefehle, die du mit dem Punktoperator aufrufst. Falls der gewählte Ausschnitt zu klein ist (insbesondere wenn man ohne zu Ziehen mit der Maus klickt), so kehrt doIt() mit einem return unverrichteter Dinge zurück, ebenfalls wenn die Eckpunkte nicht im Bildbereich liegen. Zum Abspeichern wird die Methode save() verwendet, die als letzten Parameter das Bildformat festlegt. Erlaubt sind die Werte: "bmp", "gif", "jpg", "png". |
AUFGABEN |
|