Posts by simonz
-
-
__blackjack__ Ich habe nun Deine Version mit der class mit meinen Ideen
verwurstetkombiniert.Es wäre nett, wenn Du bei Gelegenheit mal drüberschaust...
Python
Display More#!/usr/bin/env python from functools import cached_property from PIL import Image, ImageDraw, ImageFont BACKGROUND_COLOR = (0, 0, 255) HEIGHT_OVER_HORIZON_MAX = 61 # need to be adjusted to get a nice view/perspective... HEIGHT_OVER_HORIZON_DEFAULT = 45 AZIMUTH_DEFAULT = 180 # south TEXT_FONT = "NotoSans-Regular.ttf" TEXT_FONT_SIZE = 48 TEXT_COLOR = "white" TEXT_POS_H = 0 # Distance from top of image in pixels: # Ascender line (top) of the first line of text, as defined by the font # (See https://pillow.readthedocs.io/en/latest/handbook/text-anchors.html) class Layer: # TODO check if there is a better solution instead of class variables... main_layer_width = 0 main_layer_height = 0 def __init__(self, filename, relative_position=(0, 0), main_layer=False, is_sun=False): self.filename = filename self.relative_position = relative_position self.main_layer = main_layer self.is_sun = is_sun self.position = None if self.main_layer: # TODO Check for uniqueness!? Until then: The last one wins. try: Layer.main_layer_width = self.image.width Layer.main_layer_height = self.image.height except AttributeError as e: raise RuntimeError(f"At least the main layer image '{self.filename}' is required, but not found! ") from None @cached_property def image(self): try: return Image.open(self.filename).convert("RGBA") except FileNotFoundError: print(f"Warning: No image for layer '{self.filename}' found!") return None def get_position(self, width, height): return ( ( int(self.relative_position[0] * width), int(self.relative_position[1] * height), ) if self.position is None else self.position ) def paste_onto(self, image): if self.image: image.paste( self.image, self.get_position(image.width, image.height), self.image, ) # Layers topdown LAYERS = [ Layer("car.png", (0.6, 0.8)), Layer("wiese_haus.png", main_layer=True), Layer("sonne.png", is_sun=True), Layer("sky.png"), ] def get_sun_position(image, width, height, azimuth, height_over_horizon): return ( int((width - image.width) * (azimuth - 90) / 180), int( (height - image.height) * (1 - height_over_horizon / HEIGHT_OVER_HORIZON_MAX) ), ) def create_image(azimuth=AZIMUTH_DEFAULT, height_over_horizon=HEIGHT_OVER_HORIZON_DEFAULT, msg=""): main_width, main_height = Layer.main_layer_width, Layer.main_layer_height image = Image.new("RGBA", (main_width, main_height), color=BACKGROUND_COLOR) # Paste the layers from bottom to top for layer in reversed(LAYERS): if layer.is_sun: layer.position = get_sun_position( layer.image, main_width, main_height, azimuth, height_over_horizon ) layer.paste_onto(image) if msg: font = ImageFont.truetype(TEXT_FONT, TEXT_FONT_SIZE) d = ImageDraw.Draw(image) d.text((main_width//2, TEXT_POS_H), msg, fill=TEXT_COLOR, anchor="ma", font=font) return image def main(): create_image(azimuth=180, height_over_horizon=45, msg="12:00").save("new.png") if __name__ == "__main__": main()
-
[...]
Die erste Schleife öffnet alle Bilder um von einem davon die Grösse auszulesen.
==>
[...]
Da sollte man statt eines "sun"-Flags besser einfach prüfen ob der Name der Ebene "Sonne" ist.
[...]
__blackjack__ Dann ist aber diverse Bild-Aufbau-Logik fest in der Funktion create_image() verdrahtet...
Das Ziel meiner Implementierung war es, den gesamten Aufbau in die Layer-Definition zu packen und die Funktion (möglichst) allgemein zu halten.Ich werde anhand unserer nun schon vier Varianten diesbezüglich noch ein bisschen weiter experimentieren.
-
-
Hallo __blackjack__, danke für den ausführlichen Code-Roast!
Da ist ja mein heutiger Abend gerettet, das zu lesen, zu verstehen und - zumindest teilweise - zu verinnerlichen. -
Ich habe nun noch ein Textlabel hinzugefügt, z.B. für die Uhrzeit:
Aufruf:
Pythoncreate_image(azimuth=azimuth, height_over_horizon=sonnenhoehe, filename=f"azimut-{int(azimuth)}.png", msg="12:00")
Display Spoiler
Python
Display More#!/usr/bin/env python import sys from PIL import Image, ImageDraw, ImageFont HEIGHT_OVER_HORIZON_MAX = 61 # need to be adjusted to get a nice view/perspective... HEIGHT_OVER_HORIZON_DEFAULT = 45 AZIMUTH_DEFAULT = 180 # south TEXT_FONT = "NotoSans-Regular.ttf" TEXT_FONT_SIZE = 48 TEXT_COLOR = "white" TEXT_POS_H = 0 # Distance from top of image in pixels: # Ascender line (top) of the first line of text, as defined by the font # (See https://pillow.readthedocs.io/en/latest/handbook/text-anchors.html) # Layers topdown layers = [dict(name="Auto", file="car.png", rpos=(0.6, 0.8)), dict(name="Haus", file="wiese_haus.png", main=True), dict(name="Sonne", file="sonne.png", sun=True), #dict(name="Himmel", file="heaven.png"), dict(name="Hintergrund", color=(0,0,255), background=True), ] def create_image(azimuth=AZIMUTH_DEFAULT, height_over_horizon=HEIGHT_OVER_HORIZON_DEFAULT, filename=None, msg=""): def get_sunpos(): return (int((main_w - im.size[0])*(azimuth-90)/180), int((main_h - im.size[1])*(1-height_over_horizon/HEIGHT_OVER_HORIZON_MAX)) ) # first run to get the main image dimensions for f in layers: try: im = Image.open(f["file"]) except (KeyError, FileNotFoundError): im = None if f.get("main", False): if not im: print("Data error: No image for main layer found!", f["file"]) sys.exit(1) (main_w, main_h) = im.size background_color = None # second run to complete the layer infos for f in layers: try: im = Image.open(f["file"]).convert("RGBA") except (KeyError, FileNotFoundError): im = None f.update(dict(image=im, pos=(0,0))) if im: if f.get("rpos", False): f.update(dict(pos=(int(f["rpos"][0]*main_w),int(f["rpos"][1]*main_h,)))) if f.get("sun", False): f.update(dict(pos=get_sunpos())) else: if f.get("background", False): background_color = f.get("color", None) else: print("Data error: No image for layer found!", f["file"]) im = Image.new("RGBA", (main_w, main_h), color=background_color) # Paste the layers from bottom to top for layer in reversed(layers): if layer["image"]: im.paste(layer["image"], layer["pos"], layer["image"]) if msg: font = ImageFont.truetype(TEXT_FONT, TEXT_FONT_SIZE) d = ImageDraw.Draw(im) d.text((main_w//2, TEXT_POS_H), msg, fill=TEXT_COLOR, anchor="ma", font=font) if filename: im.save(filename, format=filename.rsplit(".")[-1]) def main(): create_image(azimuth=180, height_over_horizon=45, filename="new.png", msg="12:00") if __name__ == "__main__": main()
-
Ich hatte nicht richtig hingeschaut. Es wird ja Zeile 17 angemeckert...
Und dort stehen ja Defaultwerte nach dem "=". Und die müssen natürlich definiert sein. Oder Konstanten, wie in meinem ursprünglichen Script.
Also versuch's erstmal so:
-
Ach so baust Du das zusammen...
Die Zeile 83, also das create_image() gehört in die Schleife, dort wo auch das print("Sonnenhöhe..") steht.
-
Siehe mein Edit von #27
-
Wie bekomme ich deine Vorlage dazu, diese Werte zu übernehmen ?
Wer ist gemeint?
Falls ich:
In Deinem Sonnenstands-Script:
Pythonfrom image_tools import create_image ... create_image(azimuth=azimuth, height_over_horizon=sonnenhoehe, filename=f"azimut-{int(azimuth)}.png")
Mein Script aus #22 ist als image_tools.py gespeichert.
Irgendwie wird es Zeit, dass der Frühling kommt! (Edit: Oh, schade, in der Vorschau ist es nicht animiert...)
-
simonz Hab nur überflogen, aber man erstellt keine Funktion in einer Funktion.
Warum denn nicht?
Grundsätzlich schreit das hier geradezu nach einer Klasse.
Diese Schreie nehme ich immer nur aus seeeehr großer Entfernung wahr.
Denn ich denke weiterhin prozedural/funktional, aber nicht objektorientiert. Und so sind auch meine Skripte, zumindest in den ersten Versionen.Vermutlich wird Dein Code von anderen Usern noch genauer unter die Lupe genommen.
Ja gerne.
-
Ich habe mein kleines PIL-"Malprogramm" noch einmal überarbeitet.
Die Layer werden jetzt einfach konfiguriert und dann automatisch zusammengebaut.Ja, das ist nur Spielkram, aber auch ein Proof-of-Concept, jedenfalls für mich.
Display Spoiler
Python
Display More#!/usr/bin/env python import sys from PIL import Image HEIGHT_OVER_HORIZON_MAX = 61 # need to be adjusted to get a nice view/perspective... # Layers topdown layers = [dict(name="Auto", file="car.png", rpos=(0.6, 0.8)), dict(name="Haus", file="wiese_haus.png", main=True), dict(name="Sonne", file="sonne.png", sun=True), dict(name="Himmel", file="heaven.png"), dict(name="Hintergrund", color=(0,0,255), background=True), ] def create_image(azimuth=180, height_over_horizon=23, filename=None): def get_sunpos(): return (int((main_w - im.size[0])*(azimuth-90)/180), int((main_h - im.size[1])*(1-height_over_horizon/HEIGHT_OVER_HORIZON_MAX)) ) # first run to get the main image dimensions for f in layers: try: im = Image.open(f["file"]) except (KeyError, FileNotFoundError): im = None if f.get("main", False): if not im: print("Data error: No image for main layer found!", f["file"]) sys.exit(1) (main_w, main_h) = im.size background_color = None # second run to complete the layer infos for f in layers: try: im = Image.open(f["file"]).convert("RGBA") except (KeyError, FileNotFoundError): im = None f.update(dict(image=im, pos=(0,0))) if im: if f.get("rpos", False): f.update(dict(pos=(int(f["rpos"][0]*main_w),int(f["rpos"][1]*main_h,)))) if f.get("sun", False): f.update(dict(pos=get_sunpos())) else: if f.get("background", False): background_color = f.get("color", None) else: print("Data error: No image for layer found!", f["file"]) im = Image.new("RGBA", (main_w, main_h), color=background_color) # Paste the layers from bottom to top for layer in reversed(layers): if layer["image"]: im.paste(layer["image"], layer["pos"], layer["image"]) if filename: im.save(filename, format=filename.rsplit(".")[-1]) def main(): create_image(azimuth=190, height_over_horizon=23, filename="new.png") if __name__ == "__main__": main()
-
Dieses Pythonscript habe ich im Netz gefunden und etwas auf meine Bedürfnisse angepasst
Der Autor des ursprünglichen Scripts hat übrigens noch fleißig weiter daran gearbeitet: https://github.com/s-bear/sun-position
-
ich hatte halt doch eine Option übersehen. Es funktioniert !!!!
Vielleicht verrätst Du den Mitlesern noch, mit welchem Aufruf es nun klappt?
-
-
Statt [...]
Danke! Da das ein Quickie-Skript war, ist bestimmt noch viel Optimierungspotential drin!
-
Just for fun mit den Bildern aus #1:
Python
Display More#!/usr/bin/env python from PIL import Image #IMG_CAR = "car.png" # foreground IMG_SCENE = "wiese_haus.png" # main scene IMG_SUN = "sonne.png" # back of main scene #IMG_BACKGROUND = "heaven.png" # background HEIGHT_OVER_HORIZON_MAX = 61 # need to be adjusted to get a nice view/perspective... def create_image(azimut=180, height_over_horizon=23, resulting_imagefile=None, image_format="png"): #frontImage = Image.open(IMG_CAR) back_image = Image.open(IMG_SUN) #background_image = ... scene = Image.open(IMG_SCENE) scene_work = Image.open(IMG_SCENE) back_image = back_image.convert("RGBA") scene = scene.convert("RGBA") scene_work = scene_work.convert("RGBA") width = int((scene.width - back_image.width)*(azimut-90)/180) height = int((scene.height - back_image.height)*(1-height_over_horizon/HEIGHT_OVER_HORIZON_MAX)) # Paste the back_image at (width, height) and put the original scene back on the result via alpha scene_work.paste(back_image, (width, height), back_image) scene_work = Image.alpha_composite(scene_work, scene) if resulting_imagefile is not None: scene_work.save(resulting_imagefile, format=image_format) # print("scene.height:", scene.height, " back_image.height:", back_image.height, " height:", height) def main(): create_image(azimut=200, height_over_horizon=23, resulting_imagefile="new.png", image_format="png") if __name__ == "__main__": main()
ergibt z.B.:
-
Im Skript wird teilweise das falsche Datum/Zeit verwendet. datumzeit müsste args.t sein, nicht now().
Python
Display Moredef main(args): datumzeit=(datetime.datetime.now().strftime('%y-%m-%d %H:%M:%S')) az, zen, ra, dec, h = sunpos(args.t, args.lat, args.lon, args.elev, args.temp, args.p, args.dt, args.rad) ... if args.csv: ... #machine readable print('{t}, {lat:.1f}, {lon:.1f}, {elev}, {temp}, {p:.0f}, {az:.0f}, {zen:.0f}'.format( t=datumzeit, lat=args.lat, lon=args.lon, elev=args.elev,temp=args.temp, p=args.p,az=az, zen=(zen-90))) else: ... print("Berechne Sonnenposition für Zeitpunkt {t}".format(t=datumzeit))
-
-
fred0815 Das mit dem Sonnenstand finde ich interessant. Allerdings etwas heftige Mathematik...
Die Matheaufgaben sind ja schon erledigt, es geht nur um die visuelle Darstellung.
Das ist schon klar. Lese mich ja gerade erst ein...