-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
karl
committed
Nov 25, 2022
0 parents
commit e8cf714
Showing
12 changed files
with
1,562 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
.DS_Store | ||
.vscode/* | ||
__pycache__/ | ||
structmeta.egg-info/* | ||
*.zip | ||
*.jpg | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
--- | ||
title: "Structmeta" | ||
author: "Karl-Ulrich Krägelin" | ||
date: "24.11.2022" | ||
subject: "Markdown" | ||
titlepage: true | ||
footnotes-pretty: true | ||
caption-justification: raggedleft | ||
disable-header-and-footer: false | ||
footer-left: "\\hspace{1cm}" | ||
lang: "de" | ||
toc-own-page: true | ||
geometry: | ||
- margin=25mm | ||
--- | ||
|
||
Mit dem structmeta-Script lassen sich durch die Ablage von Scans in einer speziellen Ordnerstruktur DDB- bzw. Zeitungsportal konforme METS/MODS XML-Dateien herstellen. | ||
|
||
Structmeta kann METS/MODS für Monographien, Zeitschriften und Zeitungen erstellen; am besten funktioniert es auf Grund der geringen Anforderungen für Zeitungsausgaben. | ||
|
||
Das Programm benötigt zum Funktionieren neben einer speziellen Ordnerstruktur, in der die Digitalisate abgelegt sind, pro Vorgang eine Datei in der Metadaten, die für den gesamten Vorgang gelten, enthalten sind. Diese Metadaten müssen im `TOML` Format vorliegen (s.u.). | ||
|
||
Automatisch läuft eine TIF zu JPG Umwandlung. | ||
|
||
Nach Programmaufruf muss der Pfad zur `TOML` Datei ausgewählt werden und der Ordner, der bearbeitet werden soll. | ||
|
||
Die Erweiterten Funktionen sind: | ||
|
||
- **Thumbnails**: Erzeugt für jede Bilddatei kleingerechnete Thumbnail Bilder | ||
- **OCR**: Erstellt pro Bild eine ALTO-XML Datei mit Tesseract (siehe unten, Work in Progress, erfordert eine externe Installation) | ||
- **ZIP**: Erstellt zwei ZIP-Dateien für die Übergabe der Bilder und der Metadaten an die Fachstelle Bibliothek der DDB | ||
- **Rename Images**: Kann die Bilder eindeutig umbenennen, nützlich falls bspw. in jedem Unterordner Bilder mit Namen wie `01.jpg` liegen | ||
|
||
![GUI](assets/gui.png) | ||
|
||
Das Script kann auch im Kommandozeilenmodus laufen: | ||
|
||
``` | ||
python structmeta.py --ignore-gooey --help | ||
``` | ||
|
||
# Vorbemerkungen | ||
|
||
Das Programm geht davon aus, dass die Namen der Bilderdateien auf Projektebene eindeutig sind. Ist dies nicht der Fall, müssen die Bilddateien umbennant werden (Option "Rename Images"). | ||
|
||
# Metadaten | ||
|
||
Die TOML Datei besteht aus vier Ebenen: Einmal Informationen zum Datenpartner, und einmal Informationen zu den Objekten. Die Ebenen werden durch die Strings `[institution]`, `[objects]`, `[images]` und `[OCR]` getrennt. Unterhalb dieser Ebenen wird pro Zeile einem Schlüssel durch ein `=` Zeichen ein Wert zugewiesen. Zeichenfolgen werden dabei in Anführungsstriche gesetzt. Die TOML-Datei ist mit einem Plaintext-Texteditor zu bearbeiten (nicht etwa mit Word o.ä.). Mit Tools wie bspw. [TOML Lint](https://www.toml-lint.com/) kann die erzeugte TOML Datei validiert und auf Syntaxfehler geprüft werden. Das sieht dann exemplarisch so aus - hier fehlt in Zeile 16 ein einfaches Anführungszeichen: | ||
|
||
![](assets/tomllint.png) | ||
|
||
## TOML Beispiel | ||
|
||
```toml | ||
[institution] | ||
isil = "DE-1234" | ||
name = "Kreisarchiv Teststadt" | ||
logoURL = "https://www.mein-archiv.de/logo.svg" | ||
siteURL = "https://www.mein-archiv.de" | ||
contact = "[email protected]" | ||
license = "https://creativecommons.org/publicdomain/mark/1.0/" | ||
|
||
[objects] | ||
type = "newspaper" | ||
erscheinungsort = "Teststadt" | ||
verlag = "Testverlag" | ||
year_of_digitization = "2022" | ||
place_of_digitization = "Teststadt" | ||
sprache = "ger" | ||
title = "Teststätter Zeitung" | ||
|
||
[images] | ||
#imagebaseurl = "http://www.mein-archiv.de/repo/" | ||
max_dimensions = 500 | ||
jpg_compression_level = 20 | ||
|
||
[OCR] | ||
tesseract_language = "deu" | ||
#tesseract_executable = "C:/Program Files/Tesseract-OCR/tesseract.exe" | ||
``` | ||
|
||
Erweiterte Informationen zu den Elementen finden Sie unter: . | ||
|
||
# Ordnerstrukturen | ||
|
||
## Zeitungen | ||
|
||
Für Zeitungen muss die Ordner/Dateistruktur wie folgt aussehen: | ||
|
||
Auf der obersten Ebene ein Ordner der nach der [ZDB ID](https://zdb-katalog.de) der **digitalen Ausgabe** der Zeitung benannt ist, darunter pro Ausgabe ein Ordner, der mit dem Tagesdatum der Veröffentlichung in ISO (vierz Ziffern für das Jahr, Bindestrich, zwei Ziffern für Monat und Tag, getrennt durch einen Bindestrich. Also `YYYY-MM-DD`) beginnt. Darunter liegen die Scans der Ausgabe durch ihren Dateinamen in der richtigen Reihenfolge. Die Seiten sollten also irgendwie numerisch aufsteigend gekennzeichnet sein. | ||
|
||
``` | ||
- 984399-1 | ||
- 1802-01-01 | ||
- img0001.jpg | ||
... | ||
- 1802-01-02 | ||
``` | ||
|
||
## Zeitschriften (Journals) | ||
|
||
Achtung: Structmeta kann _keine Gesamtaufnahme_ erstellen, da diese von der DDB auch nicht gehostet werden könnte. Daher werden Jorunals als `monograph` angegeben. | ||
|
||
Ordner/Dateistruktur: Auf der obersten Ebene der Titel der Zeitschrift, darunter pro Jahrgang ein Ordner, der am Ende mit einem Unterstrich abgetrennt das Jahr enthält. Unter dem Ordner des Jahrgangs dann pro Strukturelement ein Ordner (bspw. pro Heft), der nach einem Unterstrich den Titel des Elements enthält (statt `Jahrgang_1808_Ausgabe Januar` wäre auch `_Ausgabe Januar` möglich oder `Zeitschrift_Jahrgang_1808_Ausgabe Januar`. In allen Fällen wird als Titel `Ausgabe Januar` erkannt): | ||
|
||
``` | ||
Testzeitschrift | ||
- Jahrgang_1807 | ||
- Jahrgang_1808 | ||
- Jahrgang_1808_Titel | ||
- Zeitschrift_1908_001.jpg | ||
- Jahrgang_1808_Inhaltsverzeichnis | ||
- Zeitschrift_1908_002.jpg | ||
- Jahrgang_1808_Ausgabe Januar | ||
- Zeitschrift_1908_003.jpg | ||
- Zeitschrift_1908_004.jpg | ||
- Zeitschrift_1908_005.jpg | ||
... | ||
- Jahrgang_1809 | ||
``` | ||
|
||
## Monographien (Work in Progress) | ||
|
||
Ordner/Dateistruktur: Auf der obersten Ebene ein Ordner unter dem die Monographien sortiert werden, darunter für jedes Werk ein Ordner. Unterhalb eines jeden Werkes dann für die Strukturelemente Ordner, in denen sich die entsprechenden Bilder befinden. | ||
|
||
Achtung: Diese Funktionalität ist noch nicht ausgereift. Außerdem fehlen für Monographien fast alle Angaben! | ||
|
||
``` | ||
Monographien | ||
- Der Testfall | ||
- Abhandlungen die Testreichen Tests betreffend | ||
- Titel | ||
- xy1000_001.jpg | ||
- Inhaltsverzeichnis | ||
- xy1000_002.jpg | ||
- Kapitel 1 / Ausgabe Januar | ||
- xy1000_003.jpg | ||
- xy1000_004.jpg | ||
- xy1000_005.jpg | ||
... | ||
- Der Zauberlehrling | ||
``` | ||
|
||
# OCR | ||
|
||
Unter Verwendung einer externen Installation von Tesseract kann für jede Bilddatei eine zugehörige ALTO XML Datei erstellt und in einer `mets:fileGrp@USE=FULLTEXT` referenziert werden. | ||
|
||
## Installation von Tesseract | ||
|
||
### Ubuntu | ||
|
||
``` | ||
sudo add-apt-repository ppa:alex-p/tesseract-ocr-devel | ||
sudo apt update | ||
sudo apt install tesseract-ocr | ||
sudo apt-get install tesseract-ocr-deu | ||
``` | ||
|
||
### Windows | ||
|
||
- siehe https://github.com/UB-Mannheim/tesseract/wiki#tesseract-installer-for-windows | ||
- Im Schritt Komponenten auswählen entsprechende benötigte Sprachpakete laden: | ||
![](assets/tessinstall1.png) | ||
![](assets/tessinstall2.png) | ||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
lxml | ||
loguru | ||
click | ||
toml | ||
pillow | ||
PyMuPDF | ||
pandas | ||
gooey | ||
pytesseract | ||
natsort |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
""" | ||
Installs: | ||
- structmeta | ||
""" | ||
|
||
from setuptools import setup | ||
from setuptools import find_packages | ||
|
||
setup( | ||
name='structmeta', | ||
version='0.5', | ||
description='', | ||
long_description="", | ||
long_description_content_type='text/markdown', | ||
author='Karl-Ulrich Krägelin', | ||
author_email='[email protected]', | ||
url='https://gitlab.gwdg.de/maps/mmservice', | ||
license='MIT', | ||
packages=find_packages(), | ||
include_package_data=True, | ||
install_requires=open('requirements.txt').read().split('\n'), | ||
entry_points={ | ||
'console_scripts': [ | ||
'structmeta=structmeta:main', | ||
] | ||
} | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import fitz # PyMuPDF | ||
import io | ||
from PIL import Image | ||
import re | ||
from pathlib import Path | ||
from gooey import Gooey, GooeyParser | ||
import os | ||
|
||
|
||
def extractImagesFromPDF(pdffile, outputfolder, compressionlevel: int): | ||
pdffile = Path(pdffile) | ||
if date := re.search(r"\d{1,}\.\d{1,}\.\d{2,}", pdffile.stem): | ||
d = re.sub(r"(\d+)\.(\d+)\.(\d+)", r"\3-\2-\1", date[0]) | ||
isodate = re.sub(r"\D(\d)$", r"-0\1", re.sub(r"\D(\d)\D", r"-0\1-", d)) | ||
elif isodate := re.search(r"\d{4}-\d{2}-\d{2}", pdffile.stem): | ||
isodate = isodate[0] | ||
else: | ||
isodate = None | ||
# open the file | ||
pdf_file = fitz.open(pdffile) | ||
# iterate over PDF pages | ||
n = 0 | ||
allpages = len(pdf_file) | ||
for page_index in range(allpages): | ||
n += 1 | ||
print(f"Speichere Datei {n} von {allpages}", flush=True) | ||
# get the page itself | ||
page = pdf_file[page_index] | ||
for image_index, img in enumerate(page.get_images(), start=1): | ||
# get the XREF of the image | ||
xref = img[0] | ||
# extract the image bytes | ||
base_image = pdf_file.extract_image(xref) | ||
image_bytes = base_image["image"] | ||
# load it to PIL | ||
image = Image.open(io.BytesIO(image_bytes)) | ||
# save it to local disk | ||
if isodate: | ||
filename = f"{isodate}--{pdffile.stem}_{page_index+1:02}.jpg" | ||
else: | ||
filename = f"{pdffile.stem}__{page_index+1:02}.jpg" | ||
image.save(Path(outputfolder, filename), "JPEG", quality=compressionlevel) | ||
|
||
|
||
@Gooey(program_name="PDF Image Extraktor", required_cols=1, default_size=(550, 450)) | ||
def cli(): | ||
parser = GooeyParser(description="0.3") | ||
parser.add_argument( | ||
"Ordner", help="Bitte den Ordner mit PDFs auswählen", widget="DirChooser" | ||
) | ||
parser.add_argument("-c", "--compression", type=int) | ||
args = parser.parse_args() | ||
inputfolder = Path(args.Ordner) | ||
compressionlevel = args.compression | ||
if compressionlevel is None: | ||
compressionlevel = 90 | ||
for f in inputfolder.glob("*.pdf"): | ||
print(f"Bearbeite {f}", flush=True) | ||
outputfolder = Path(os.getcwd(), f.stem) | ||
outputfolder.mkdir(exist_ok=True) | ||
extractImagesFromPDF(f, outputfolder, compressionlevel) | ||
print(f"Alle PDFs konvertiert.", flush=True) | ||
|
||
|
||
if __name__ == "__main__": | ||
cli() |
Oops, something went wrong.