Wie extrahiere ich ein eingebettetes Bild aus einer SVG-Datei?

26

Ich habe eine SVG-Datei, die mindestens ein eingebettetes JPG / PNG-Bild enthält. Ich möchte die JPG / PNG-Bilder aus dieser SVG-Datei extrahieren und auf der Festplatte speichern.

Ich füge das inkscapeTag hinzu, da es das Programm ist, mit dem ich SVG-Dateien bearbeite, aber ich akzeptiere auch Lösungen, die andere Tools verwenden.

Denilson Sá Maia
quelle
1
Wenn nichts anderes, könnte Python dies wahrscheinlich mit einem benutzerdefinierten Klebstoff unter Verwendung von lxml und PIL (oder einem gleichwertigen) tun.
Keith
@ Keith, tatsächlich habe ich gerade ein Python-Skript geschrieben , um diese Frage zu lösen. Es verwendet die eingebaute xml.etreeBibliothek.
Denilson Sá Maia

Antworten:

30

Meine eigene Lösung (oder ... Problemumgehung):

  1. Wählen Sie das Bild in Inkscape aus
  2. Öffnen Sie das eingebaute XML Editor( Shift+ Ctrl+ X)
  3. Wählen Sie das xlink:hrefAttribut aus, das das Bild als Daten enthalten soll: URI
  4. Kopieren Sie die gesamte data:URI
  5. Fügen Sie diesen data:URI in einen Browser ein und speichern Sie ihn dort.

Alternativ kann ich die SVG-Datei in einem beliebigen Texteditor öffnen, die data:URI suchen und von dort kopieren.

Obwohl diese Lösung funktioniert, ist sie etwas umständlich und ich würde gerne eine bessere lernen.

Denilson Sá Maia
quelle
2
+1 - Ich habe ein 3,5-MB-Bild mit dieser Methode exportiert, was eine Weile gedauert hat, aber funktioniert hat. Irgendwie hat die Funktion "Bild extrahieren" bei mir nicht funktioniert.
Martin
Lesen Sie hierzu auch ein Befehlszeilen-Python-Skript .
Denilson Sá Maia
17

Stattdessen gibt es eine bessere Lösung:

Gehen Sie zu Extensions -> Images -> Extract Image..., dort können Sie das ausgewählte Rasterbild als Datei speichern. Allerdings funktioniert diese Erweiterung seltsam und irgendwie ziemlich langsam (aber vollkommen gut).

Noch ein Hinweis: Diese Erweiterung ist umständlich und stirbt lautlos bei unterschiedlich großen Bildern. Mit einer großen Anzahl von Rasterbildern kann die Speichernutzung von inkscape auf entsetzliche Ebenen gesteigert werden (z. B. 3 GB nach nur einer Handvoll extrahierter Bilder).

Da ich ungefähr 20 SVG-Dateien mit jeweils ungefähr 70 Rasterbildern habe, von denen jedes mindestens 1 MB groß ist, brauchte ich eine andere Lösung. Nach einer kurzen Überprüfung mit Denilson Sá tip habe ich das folgende PHP-Skript entwickelt, das Bilder aus SVG-Dateien extrahiert:

#!/usr/bin/env php
<?php

$svgs = glob('*.svg');

$existing = array();

foreach ($svgs as $svg){
    mkdir("./{$svg}.images");
    $lines = file($svg);
    $img = 0;
    foreach ($lines as $line){
        if (preg_match('%xlink:href="data:([a-z0-9-/]+);base64,([^"]+)"%i', $line, $regs)) {
            $type = $regs[1];
            $data = $regs[2];
            $md5 = md5($data);
            if (!in_array($md5, $existing)) {
                $data = str_replace(' ', "\r\n", $data);
                $data = base64_decode($data);
                $type = explode('/', $type);
                $save = "./{$svg}.images/{$img}.{$type[1]}";
                file_put_contents($save, $data);
                $img++;
                $existing[] = $md5;
            }
        } else {
            $result = "";
        }
    }
}

echo count($existing);

Auf diese Weise kann ich alle gewünschten Bilder abrufen, und md5 erspart mir das wiederholte Abrufen von Bildern.

Ich wette, es muss einen anderen Weg geben, der viel einfacher ist, aber es liegt an den inkscape-Entwicklern, es besser zu machen.

Johnny_Bit
quelle
Hinweis: Ihr Skript unterstützt nur eine data:URL pro Zeile und keine Zeilenumbrüche innerhalb des href-Attributs (inkscape fügt sie für Daten-URLs hinzu, und die base64-Spezifikation schreibt sogar vor, dass Zeilen nicht länger als 76 Zeichen sein dürfen ). Nettes Skript für einen schnellen Hack, aber es funktioniert nicht mit allen Arten von SVG.
Denilson Sá Maia
@Johnny_Bit +1 für die Verwendung von md5 sum, um das Duplizieren von Dateien zu verhindern. Ich verbessere dein Skript weiter unten .
Ivan Z
gut, märz 2019 und einfach grand mit einem einigermaßen großen image gearbeitet. Und ziemlich alten Laptop / Ubuntu / Inkscape 0.48.4. Vielen Dank!
Gaoithe
9

Schließlich habe ich Jahre später ein Skript geschrieben, um alle Bilder aus einer SVG-Datei korrekt zu extrahieren und den SVG-Code mithilfe einer geeigneten XML-Bibliothek zu analysieren.

http://bitbucket.org/denilsonsa/small_scripts/src/tip/extract_embedded_images_from_svg.py

Dieses Skript wurde für Python 2.7 geschrieben, sollte sich aber recht einfach in Python 3 konvertieren lassen. Noch besser ist, dass nach der Konvertierung in Python 3.4 aufgrund der in dieser Version eingeführten neuen Funktionen etwa 50 Zeilen gelöscht werden können.

Denilson Sá Maia
quelle
Danke, da es funktioniert. Aber es ist viel langsamer als die PDF-Umgehung. Haben Sie über Parallelverarbeitung nachgedacht? Derzeit verwendet das Skript nur einen einzelnen CPU-Kern / Thread.
DanMan
@DanMan Leider ist die Parallelschaltung keine magische Lösung, um etwas zu beschleunigen. Ich muss den Code profilieren, um den Engpass zu identifizieren. Wenn der Engpass beim XML-Parsing liegt, kann dieser Teil leider nicht parallel ausgeführt werden. Können Sie mir bitte per E-Mail die genauen SVG-Dateien senden, die zu langsam sind? Wann immer ich etwas Zeit habe, kann ich die Aufführung untersuchen.
Denilson Sá Maia
Ja, ich habe es selbst versucht, und es stellte sich heraus, dass das XML-Parsing der langsame Teil ist, nicht das Dekodieren der Bilder. Das heißt, cElementTreesoll schneller sein. Aber vielleicht funktioniert so etwas wie Sax auch besser.
DanMan
@DanMan cElementTreeist wahrscheinlich schneller. In Python 3.3 sind jedoch beide gleich . Irgendwann werde ich dieses Skript wahrscheinlich auf Python 3 aktualisieren.
Denilson Sá Maia
5

Als weitere Problemumgehung können Sie das Dokument als PDF speichern und anschließend mit Inkscape öffnen.

Deaktivieren Sie "Bilder einbetten" und Bingo, alle PNGs / JPEGs werden in Ihr Home-Verzeichnis ausgespuckt.

Chaotisch, aber schneller, als mit den Daten herumzuspielen: URL.

Nicholas Wilson
quelle
Wo haben Sie die Option "Bilder einbetten" gefunden?
mik01aj
1
Wenn Sie das PDF-Dokument in inkscape öffnen, wird es im nächsten Dialogfeld angezeigt.
Nicholas Wilson
Ich hatte ein PDF, aus dem ich versuchte, ein Bild durch Importieren in Inkscape zu extrahieren. In diesem Fall ist es noch praktischer, dies beim Import und nicht nach dem Import zu tun .
user149408
Ich bin nicht sicher, aber auf diese Weise scheinen eingebettete ICC-Profile dabei verloren zu gehen. In die Bilder, die ich direkt aus der SVG über dieses Python-Skript extrahiert habe, sind ICC-Profile eingebettet.
DanMan
1

Ich verbessere das PHP-Skript von @Johnny_Bit . Neue Version des Skripts kann Svg mit neuen Zeilen verwenden. Es extrahiert mehrere Bilder aus einer SVG-Datei und speichert sie in externen PNG-Dateien. Svg- und png-Dateien befinden sich im Verzeichnis 'svg', können jedoch in der Konstanten 'SVG_DIR' geändert werden.

<?php

define ( 'SVG_DIR', 'svg/' );
define ( 'SVG_PREFIX', 'new-' );

$svgs = glob(SVG_DIR.'*.svg');
$external = array();
$img = 1;

foreach ($svgs as $svg) {
    echo '<p>';
    $svg_data = file_get_contents( $svg );
    $svg_data = str_replace( array("\n\r","\n","\r"), "", $svg_data);
    $svg_file = substr($svg, strlen(SVG_DIR) );
    echo $svg_file.': '.strlen($svg_data).' ????';

    if ( preg_match_all( '|<image[^>]+>|', $svg_data, $images, PREG_SET_ORDER) ) {
        foreach ($images as $image_tag) {

            if ( preg_match('%xlink:href="data:([a-z0-9-/]+);base64,([^"]+)"%i', $image_tag[0], $regs) ) {
                echo '<br/>Embeded image has benn saved to file: ';

               $type = $old_type = $regs[1];
               $data = $old_data = $regs[2];
               $md5 = md5($data);
               if ( array_key_exists($md5, $external) ) {
                $image_file = $external[$md5];
               } else {
                    $data = str_replace(" ", "\r\n", $data);
                    $data = base64_decode($data);
                    $type = explode('/', $type);
                    $image_file = substr( $svg_file, 0, strlen($svg_file)-4 ) . '-' . ($img++) . '.png';
                    file_put_contents(SVG_DIR.$image_file, $data);
                    $external[$md5] = $image_file;
               }
               echo $image_file;
               $svg_data = str_replace('xlink:href="data:'.$old_type.';base64,'.$old_data.'"', 'xlink:href="'.$image_file.'"', $svg_data);
            }
        }
        file_put_contents(SVG_DIR.SVG_PREFIX.'.svg', $svg_data);
    }

   echo '</p>';
}

?>
Ivan Z
quelle
0

Öffnen Sie Ihre Datei in Inkscape und wählen Sie die zu exportierende Bitmap aus. Klicken Sie auf Datei-> Bitmap exportieren (Strg + Umschalt + E), um nur die ausgewählte Bitmap zu exportieren.

Chris
quelle
Diese Lösung gefällt mir nicht, weil sie das Bild neu codiert. Ich würde eine Lösung vorziehen, die das Bild in seinem ursprünglichen Format extrahiert.
Denilson Sá Maia
1
Ja, Inkscape scheint das Bild neu zu kodieren, speichert jedoch standardmäßig PNG-Bilder. Ich gehe also davon aus, dass die Umcodierung zumindest verlustfrei ist.
Chris
1
Nicht wirklich. Das eingebettete Bild hat möglicherweise Transformationen (Skalierung, Drehung ...), wurde möglicherweise abgeschnitten oder ist mir noch etwas anderes nicht bekannt. Inkscape exportiert das ausgewählte Objekt nach Anwendung all dieser Transformationen, was bedeutet, dass diese Lösung nicht gerade verlustfrei ist.
Denilson Sá Maia