from io import TextIOWrapper class PNMBild: def __init__(self, dateiName, schwellwert=0.9): # Python scheint nicht einfach eine Zahl nach der anderen # lesen zu können (egal ob Zeilenumbruch oder Leerschlag) # Darum hier ein Nachbau dafür, inkl. Handling für Kommentare def nextInt(file: TextIOWrapper): zeichen = file.read(1) # Ein Zeichen einlesen if not zeichen: # Datei zu Ende return False while zeichen=="#": # Kommentar, ignorieren file.readline() # Ganze Zeile lesen zeichen = file.read(1) # Erste Zeichen nächster Zeile if not zeichen: # Datei zu Ende return False while zeichen==" " or zeichen=="\t" or zeichen=="\n": # Leerschläge etc. ignorieren. zeichen = file.read(1) # Nächstes Zeichen if zeichen<"0" or zeichen>"9": # Keine Ziffer? Das ist ein Fehler in der Datei raise RuntimeError(f"Hmpf... keine Ziffer {zeichen} bei Byte {file.tell()}") zahl = zeichen # Zahl aufbauen while True: zeichen = file.read(1) if zeichen>="0" and zeichen<="9": # Weitere Ziffern? Hinzufügen! zahl+=zeichen else: break # Keine Ziffer, dann fertig Zahl return int(zahl) # Zeichenkette in Zahl umwandeln, Resultat zurückgegeben. # Bilddatei zum Einlesen öffnen with open(dateiName, "r") as pnm: # Erste Zeile lesen, Zeilenumbruch entfernen format = pnm.readline().strip() if format!="P3": # Keine P3-Datei? Programm abbrechen raise RuntimeError(f"Sorry, keine P3-Datei. Erste Zeile ist {format}") print("OK: P3-Format") # Format und Farbumfan auslesen self.breite = nextInt(pnm) self.hoehe = nextInt(pnm) farbumfang = nextInt(pnm) print(f"Breite: {self.breite} x Höhe {self.hoehe}, Farbumfang: {farbumfang}") self.data = [] # Leeres Array for y in range(self.hoehe): zeile = [] # Aktuelle Zeile for x in range(self.breite): # Durchschnitsswert der 3 Farbkanäle pix = sum([nextInt(pnm) for f in range(3)])/farbumfang/3 # 1 ist schwarz, 0 ist weiss (warum auch immer) if pix>schwellwert: zeile.append(1) else: zeile.append(0) self.data.append(zeile) # Umwandlung als ASCII-Art String def __str__(self): aa = "" for y in range(self.hoehe): for x in range(self.breite): if (self[x,y]==1): aa += " " else: aa += "##" aa += "\n" return aa def __getitem__(self, x): if type(x) is list or type(x) is tuple: return self.data[x[1]][x[0]] else: raise RuntimeError("use pnmbild[x,y], not pnmbild[y][x] (or use pnmbild.data[y][x])") def p1save(self, dateiName): with open(dateiName,"w") as pnm: pnm.write("P1\n") pnm.write("# ASCII Bitmap siehe https://de.wikipedia.org/wiki/Portable_Anymap\n") pnm.write(f"{self.breite} {self.hoehe}\n") for y in range(self.hoehe): # 0 ist in P1-Format weiss, 0 schwarz, darum 1-x pnm.write(" ".join(map(lambda x:str(1-x), self.data[y]))) pnm.write("\n") # Nur ausführen, wenn die Datei direkt ausgeführt wird, nicht aber wenn sie nur eingebunden wird. if __name__=="__main__": # P3 Bild einlesen und auf 0/1 herunterrechnen bild = PNMBild("herz.pnm") # Bild daten ausgeben (Liste [y][x]) print(bild.data) # ASCII-Art vom Bild ausgeben (automatischer Aufrunf von __str__() ) print(bild) # Heruntergerechnetes Bild im P1 Format speichern. bild.p1save("herz-bw.pnm")