Batch conversation von BMP zu PNG mit Extras

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
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Hi,
ich dachte eigentlich, es wäre einfacher, aber meine Aufgabe hat einige Stolpersteine.

Ich habe folgende Ausgangssituation: ich habe auf mehreren PC identische oder zumindest ähnliche Verzeichnisstrukturen mit Bildern im BMP Format. Die synchronisiere ich auf meinen lokalen PC. Jeder Remote PC muss in ein eigenes Verzeichnis gespeichert werden.

Jetzt möchte ich die Bilder rekursiv nach PNG konvertieren. Das ist noch nicht das eigentliche Problem. Das Problem ist, dass die Verzeichnisstruktur erhalten bleiben soll und die Unterverzeichnisse der Remote PC in ein gemeinsames Verzeichnis zusammengefasst werden. Ich Versuche ein Beispiel.

PC1 hat die Unterverzeichnisse foo1, foo2 und foo3. Jedes dieser Verzeichnisse hat weitere Unterverzeichnisse.

PC2 hat die Unterverzeichnisse foo1, foo2 und foo4 usw

Ich möchte jetzt alle PNG Bilder, die in den foo1 Ordnern gespeichert sind, in einen gemeinsamen foo1 Ordner auf dem lokalen PC konvertieren, egal von welchem Remote PC.

Auf dem Lokalen PC soll es also jeweils nur ein foo1, foo2, foo3 und foo4 Verzeichnis geben, in denen die Bilder von allen Remote PCs gespeichert sind.

Last but not least sollen die original Bilder gelöscht und mit leeren Dateien mit gleichem Dateinamen ersetzt werden, um den Speicherplatz frei zu geben, aber eine weitere Synchronisation zu ermöglichen.

Durch die Verzeichnisse zu wandern und Bilder konvertieren bekomme ich hin. Was mir Probleme macht ist das abspeichern am richtigen Zielort. Klingt eigentlich banal, aber verknotet mir gerade das Hirn. Vermutlich ist es total simpel, aber ich komme nicht drauf.

Wie kann ich den Knoten lösen?

Ach ja, ich möchte das in pure Python machen damit es sowohl unter Windows als auch Linux funktioniert.

Schönen Sonntag
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Das Problem löst sich, wenn du den Umstand weg lässt, dass es mehrere Clients sind. Es ändert sich ja nur das Basisverzeichnis für die Quelldateien. Die anderen Pfade sind relativ zu diesem. Sowohl zum Basisverzeichnis der Quelle als auch des Ziels.
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

sparrow hat geschrieben: Sonntag 16. Januar 2022, 08:17 Das Problem löst sich, wenn du den Umstand weg lässt, dass es mehrere Clients sind. Es ändert sich ja nur das Basisverzeichnis für die Quelldateien. Die anderen Pfade sind relativ zu diesem. Sowohl zum Basisverzeichnis der Quelle als auch des Ziels.
Das ist klar. Aber ich stehe wie gesagt gerade komplett auf dem Schlauch. Grundsätzliche Frage: was eignet sich besser? os.walk, glob oder etwas anderes? Und wie erstelle ich nicht vorhandene Verzeichnisse?
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Nach einer Tasse Kaffee hat sich der Knoten etwas gelöst, aber mir ist ein anderes Problem aufgefallen, das mein Vorhaben gerade generell sabotiert.

Trotzdem noch einen schönen Sonntag
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Der Vollständigkeit halber hier mal meine Lösung. Die Variablennamen müssten eventuell noch etwas überarbeitet werden ;)

Code: Alles auswählen

import os
from PIL import Image
import glob
import re

images = glob.glob('PC?/**/*.bmp', recursive=True)

for img in images:
    # Skip if file is too small
    if os.path.getsize(img) < 10:
        continue

    file = os.path.split(img)

    new_file = (re.sub(r"PC\d+", "PNG",
                file[0]), file[1].replace(".bmp", ".png"))

    # Skip if file already exists
    if os.path.exists(os.path.join(new_file[0], new_file[1])):
        continue

    # Create dir if not exists
    os.makedirs(new_file[0], exist_ok=True)

    # Convert BMP to PNG and save in new dir
    Image.open(img).save(os.path.join(new_file[0], new_file[1]))

    print(os.path.join(new_file[0], new_file[1]))

    # Remove original file and replace with empty file
    os.remove(img)
    os.mknod(img)
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Statt `os` und `glob` benutzt man `pathlib.Path`. Und auf pfade geht man auch nicht mit Stringoperationen los, wie `re`oder `replace`, sondern nutzt das, was pathlib.Path so bietet.
Man setzt auch nicht an vier verschiedenen Stellen den Gesamtdateinamen zusammen, sondern speichert das Ergebnis einmal in einer Variable.

Code: Alles auswählen

from pathlib import Path
from PIL import Image

INPUT_PATH = Path('.')
OUTPUT_PATH = Path('PNG')

for pc_path in INPUT_PATH.glob('PC?'):
    for imagefilename in pc_path.glob('**/*.bmp'):
        # Skip if file is too small
        if imagefilename.stat().st_size < 10:
            continue

        relative_imagefilename = imagefilename.relative_to(pc_path)
        new_imagefilename = (OUTPUT_PATH / relative_imagefilename).with_suffix('.png')

        # Skip if file already exists
        if new_imagefilename.exists():
            continue
        new_imagefilename.parent.mkdir(parents=True, exist_ok=True)
        image = Image.open(imagefilename)
        image.save(new_imagefilename)

        print(new_imagefilename)

        # Remove original file and replace with empty file
        imagefilename.write_bytes(b"")
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Sirius3 hat geschrieben: Sonntag 16. Januar 2022, 11:30 Statt `os` und `glob` benutzt man `pathlib.Path`. Und auf pfade geht man auch nicht mit Stringoperationen los, wie `re`oder `replace`, sondern nutzt das, was pathlib.Path so bietet.
Man setzt auch nicht an vier verschiedenen Stellen den Gesamtdateinamen zusammen, sondern speichert das Ergebnis einmal in einer Variable.
Das ich mehrfach joine weiß ich. Werde ich bei Gelegenheit ändern. pathlib schaue ich mir auch einmal an, danke.
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Nochmal danke für die Überarbeitung. Ich hab noch etwas eingefügt, um ältere Dateien komplett zu löschen.

Code: Alles auswählen

        fname = imagefilename.name
        file_date = fname.split("_")[3]
        year, month, day = file_date[:4], file_date[4:6], file_date[6:]
        now = datetime.datetime.now()
        then = datetime.datetime(int(year), int(month), int(day))
        age = (now - then).days
        if age > 10 and imagefilename.stat().st_size < 10:
            imagefilename.unlink()
            print(fname + " deleted")
            continue
In dem Dateinamen sind alle Infos enthalten und mit "_" getrennt. Das Datum steht im Format yyyymmdd an Position [3]. Da beim überschreiben der Original Datei auch das Datum geändert wird kann ich das nicht mehr verwenden. Deshalb hab ich das so gelöst. Hat da noch jemand einen Verbesserungsvorschlag?
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Zum Parsen vom Datum gibt es strptime:

Code: Alles auswählen

file_date = imagefilename.name.split('_')[3]
then = datetime.datetime.strptime(file_date, '%Y%m%d')
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Sirius3 hat geschrieben: Sonntag 16. Januar 2022, 14:22 Zum Parsen vom Datum gibt es strptime:

Code: Alles auswählen

file_date = imagefilename.name.split('_')[3]
then = datetime.datetime.strptime(file_date, '%Y%m%d')
Super, danke. Sieht etwas eleganter aus und spart nochmal zwei Zeilen. Das Script tut was es soll. Da kann ich morgen in der Realtest gehen.

Einziges Problem ist, dass die Dateien auf den Remote Rechnern nach einer Weile gelöscht werden und ich darf keine alten und erst recht keine leeren Dateien zurück synchronisieren. Die Synchronisation läuft immer in beide Richtungen, es sei denn ich kann das irgendwie verhindern.
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Ich hab noch eine Frage zu pathlib (bin gerade am Handy, deshalb kein c&p vom Code). Ich möchte alle PNG Dateien in einem Verzeichnis mit OpenCV bearbeiten.

Ich lege mit Path() einen relativen Pfad an, hole mir p.glob("*.PNG") alle Dateien und laufe mit einer for loop über die Liste. So weit, so gut.

Einziges Problem: ich muss mit str() den String aus dem Objekt holen, damit ich die Datei mit cvw.imread() laden kann. Ist das der "offizielle" weg oder gibt es da eine Methode, die einen String liefert? resolve() und absolute() liefern ja auch nur Objekte.

Übersehe ich da was?
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nein. Das ist (noch) so. Bei den meisten der mitgelieferten Bibliotheken gehts inzwischen auch ohne. Aber für Pakete dritter muss man das oft nicht machen.
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Ok, danke. Manche Bibliotheken packen sich den String also selbst aus, andere nicht. Gut zu wissen
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Antworten