Einfaerben und verkleinern einer .png mit einer .csv- Datenbasis

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
RidarTV
User
Beiträge: 1
Registriert: Donnerstag 2. Februar 2023, 11:13

Hallo :) - mein Problem erkläre ich unter dem Code :D

Ich habe einen Ordner, dort liegt meine .py Datei, eine "export.csv" Datei und gaaaaanz viele .png Dateien.
Alle Bilder sind fast weiß, mit einem teilweise transparenten Hintergrund. Ich möchte jetzt über meine .py Datei die Bilder, welche in der .csv Datei liegen über einen RGB Code einfärben.

In der .csv Datei liegt jeweils der Name der .png Dateien in Spalte 1 und der RGB Farbcode in Spalte 2.

Hier ein Beispiel, aus Notepad++ rauskopiert:
NameDatei;RGBFarbcode
41012.png;226,0,116

Und hier ist mein Code:

Code: Alles auswählen

import csv
import os
from PIL import Image

# Lesen der CSV-Datei
with open('export.csv', 'r') as file:
    reader = csv.reader(file, delimiter=';')
    data = list(reader)

# Verarbeitung der Daten aus .CSV-Datei
for row in data:
    file_name = row[0]
    rgb_strings = row[1:]  # Liste aller RGB-Farbkodierungen

# Überprüfe RGB Code in .CSV-Datei
    for i, rgb_string in enumerate(rgb_strings):
        try:
            rgb = tuple(map(int, rgb_string.split(',')))
        except ValueError:
            print(f"Ungültige RGB-Farbkodierung in Zeile {data.index(row) + 1}, Spalte {i + 2}: {rgb_string}")
            continue
        
        # Öffnen des Bildes
        image = Image.open(file_name)
        
        # weiße Stellen färben
        data = image.getdata()
        new_data = []
        for item in data:
            if item == (255, 255, 255):
                new_data.append(rgb)
            else:
                new_data.append(item)
        image.putdata(new_data)
        
        # Verkleinern der Kopie des Bildes um 50%
        image = image.resize((round(image.width / 2), round(image.height / 2)))
        
        # Speichern der Kopie des Bildes
        new_file_name = os.path.join(r"C:\Users\A200017577\Desktop\AM\PP Heatmap Grafik\Gebietszuweisung\AN Karte\Test", f"{file_name.split('.')[0]}_colored.png")
        os.makedirs(os.path.dirname(new_file_name), exist_ok=True)
        if os.path.exists(new_file_name):
            os.remove(new_file_name)
        image.save(new_file_name)

print("Alle Bilder wurden modifiziert und gespeichert.")

Mit diesem Code wird zwar eine Kopie der Bildes erstellt, jedoch werden keine Weiß-Teile der .png eingefärbt.
Ich programmiere ich Spyder (Python 3.9).

Dies ist außerdem mein erster Code, also bitte fragt, falls Ihr weitere Informationen braucht.


Ich bedanke mich vielmals für eure Antworten :D

Liebe grüße
Kevin
Benutzeravatar
__blackjack__
User
Beiträge: 13007
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@RidarTV: Also erst mal ist die zweite ``for``-Schleife über die `rgb_strings` sinnlos, denn das ist ja grundsätzlich immer nur *ein* Element. Eine Schleife über eine Liste die grundsätzlich immer nur ein Element enthält ist keine sinnvolle Schleife, sondern macht den Code nur unnötig komplizierter.

In der Schleife wird `data` plötzlich an einen anderen Wert gebunden, im Fehlerfall ist da eber Code der versucht über `data` an den Wert zu kommen über den die Schleife läuft, was natürlich dann falsch ist, denn in den Pixeldaten wird man den aktuellen Datensatz aus der CSV-Datei nicht mit `index()` finden können.

Man muss die CSV-Datei hier auch gar nicht komplett in den Speicher einlesen, sondern kann die einfach Datensatz für Datensatz beim einlesen abarbeiten und die Datensatznummer mit `enumerate()` generieren, für eine eventuelle Fehlermeldung, statt die Datensätze linear noch mal nach dem aktuellen Datensatz zu durchsuchen.

Beim öffnen von Textdateien sollte man immer die Kodierung explizit angeben, damit da nicht falsch ”geraten” werden kann. Und bei CSV-Dateien muss man das `newline`-Argument gemäss der Dokumentation angeben.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht. Ein ``# Öffnen des Bildes`` vor einer Codezeile mit einem `Image.open()`-Aufruf bringt dem Leser beispielsweise keinerlei zusätzliche Erkenntnis.

Der Code für das einfärben geht deutlich kompakter. Und da ist vielleicht auch schon ein Problem in dem Code: Du schreibst was von Transparenz. Ein Pixel mit Transparenz hat vier Werte, die sind immer ungleich drei Werte wo nur RGB kodiert ist. Da willst Du dann wahrscheinlich nur die ersten drei Werte vergleichen und den vierten einfach übernehmen, falls die Transparenz erhalten bleiben soll.

Statt `round()` könnte man einfach eine Ganzzahldivision machen.

Testen ob der Dateiname schon existiert und falls ja löschen, kann man sich sparen wenn man im nächsten Schritt sowieso eine neue Datei unter diesem Namen speichert.

Pfadoperationen macht man in neuem Code nicht mehr mit `os.path` & Co, sondern mit dem `pathlib`-Modul.

Den Zielpfad muss man auch nur einmal angelegen wenn der sich nicht ändert.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import csv
from pathlib import Path

from PIL import Image

TARGET_PATH = Path(
    R"C:\Users\A200017577\Desktop\AM\PP Heatmap Grafik\Gebietszuweisung\AN Karte\Test"
)


def main():
    with open("export.csv", "r", encoding="ascii", newline="") as file:
        TARGET_PATH.mkdir(parents=True, exist_ok=True)

        for record_number, (filename, rgb_string) in enumerate(
            csv.reader(file, delimiter=";"), 1
        ):
            file_path = Path(filename)
            try:
                rgb = tuple(map(int, rgb_string.split(",")))
            except ValueError:
                print(
                    f"Ungültige RGB-Farbkodierung in Datensatz"
                    f" #{record_number}, Spalte 2: {rgb_string!r}."
                )
            else:
                with Image.open(file_path) as image:
                    if image.mode not in ["RGB", "RGBA"]:
                        print(
                            f"Bild liegt nicht im RGB(A)-Format vor:"
                            f" {file_path!r}, Format {image.mode!r}."
                        )
                    else:
                        image.putdata(
                            [
                                rgb + pixel[3:]
                                if pixel[:3] == (255, 255, 255)
                                else pixel
                                for pixel in image.getdata()
                            ]
                        )
                        image.resize(
                            (image.width // 2, image.height // 2)
                        ).save(TARGET_PATH / f"{file_path.stem}_colored.png")

    print("Alle Bilder wurden modifiziert und gespeichert.")


if __name__ == "__main__":
    main()
Ich persönlich finde es schon ein bisschen zu lang/verschachtelt für eine Funktion. Das Verarbeiten *eines* Bildes könnte man beispielsweise gut in eine eigene Funktion auslagern.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
nezzcarth
User
Beiträge: 1632
Registriert: Samstag 16. April 2011, 12:47

Also falls es nur darum geht, die Aufgabe zu lösen und du ein Linux (oder das Linux Subsystem für Windows) zur Verfügung hast, kannst du mit awk und ImageMagick sowas in der Richtung versuchen (Vorsicht, am besten vorab mit Testdaten probieren):

Code: Alles auswählen

awk 'BEGIN{FS=";"}NR>1{system("convert " $1 " -fuzz 99% -fill \"rgb(" $2 ")\" -opaque white " $1 ".new.png")}' daten.csv
(s.a.: https://imagemagick.org/script/convert.php)
Antworten