Come estrarre un'immagine incorporata da un file SVG?


26

Ho un file SVG che contiene almeno un'immagine JPG / PNG incorporata all'interno. Voglio estrarre le immagini JPG / PNG da quel file SVG e salvarle sul disco.

Sto aggiungendo il inkscapetag in quanto è il programma che utilizzo per modificare i file SVG, ma accetto anche soluzioni che utilizzano altri strumenti.


1
Se non altro, Python potrebbe probabilmente farlo con un po 'di colla personalizzata usando lxml e PIL (o equivalente).
Keith,

@Keith, in effetti, ho appena scritto uno script Python per risolvere questa domanda. Utilizza la xml.etreelibreria integrata .
Denilson Sá Maia,

Risposte:


30

La mia soluzione (o ... soluzione alternativa):

  1. Seleziona l'immagine in Inkscape
  2. Apri il built-in XML Editor( Shift+ Ctrl+ X)
  3. Seleziona l' xlink:hrefattributo, che conterrà l'immagine come dati: URI
  4. Copia l'intero data:URI
  5. Incolla l' data:URI in un browser e salvalo da lì.

In alternativa, posso aprire il file SVG in qualsiasi editor di testo, individuare l' data:URI e copiarlo da lì.

Sebbene questa soluzione funzioni, è un po 'ingombrante e mi piacerebbe impararne una migliore.


2
+1 - Ho esportato un'immagine da 3,5 MB usando questo metodo che ha richiesto un po 'di tempo ma ha funzionato. In qualche modo la funzione "Estrai immagine" non ha funzionato per me.
Martin,

Si prega di consultare anche uno script Python da riga di comando per questo scopo.
Denilson Sá Maia,

17

C'è invece una soluzione migliore:

vai a Extensions -> Images -> Extract Image..., lì puoi salvare l'immagine raster selezionata come file. Tuttavia questa estensione funziona in modo strano e in qualche modo funziona piuttosto lentamente (ma perfettamente bene).

Un'altra nota: questa estensione è ingombrante e muore silenziosamente su varie immagini di grandi dimensioni. Inoltre, con un gran numero di immagini raster può aumentare l'utilizzo della memoria di inkscape a livelli orrendi (come 3 GB dopo solo una manciata di immagini estratte).

Dato che ho circa 20 file svg con circa 70 immagini raster al loro interno, ciascuna delle dimensioni di almeno 1 MB, avevo bisogno di una soluzione diversa. Dopo un breve controllo usando il suggerimento di Denilson Sá ho ideato il seguente script php, che estrae immagini da file svg:

#!/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);

In questo modo posso ottenere tutte le immagini che desidero e md5 mi evita di ottenere immagini ripetute.

Scommetto che ci deve essere un altro modo molto più semplice, ma spetta agli sviluppatori di inkscape farlo meglio.


Nota: lo script supporta solo un singolo data:URL per riga e non supporta le nuove righe all'interno dell'attributo href (inkscape le aggiunge per gli URL dei dati e le specifiche base64 impongono addirittura che le righe non debbano essere più lunghe di 76 caratteri ). Bel copione per un trucco rapido, ma non funziona con tutti i tipi di SVG.
Denilson Sá Maia,

@Johnny_Bit +1 per l'uso della somma md5 per evitare la duplicazione dei file. I imrove lo script di seguito .
Ivan Z,

bene, marzo 2019 e ha funzionato alla grande con un'immagine ragionevolmente grande. E piuttosto vecchio laptop / ubuntu / inkscape 0.48.4. Grazie!
martedì

9

Infine, anni dopo, ho scritto uno script per estrarre correttamente tutte le immagini da un file SVG, utilizzando una libreria XML appropriata per analizzare il codice SVG.

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

Questo script è scritto per Python 2.7 ma dovrebbe essere abbastanza facile da convertire in Python 3. Ancora meglio, dopo la conversione in Python 3.4 possono essere eliminate circa 50 linee, grazie alle nuove funzionalità introdotte in quella versione.


Grazie, dal momento che funziona. Ma è molto più lento della soluzione alternativa in PDF. Hai pensato all'elaborazione parallela? Al momento, lo script utilizza solo un singolo core / thread della CPU.
DanMan,

@DanMan Sfortunatamente, renderlo parallelo non è una soluzione magica per accelerare nulla. Avrei bisogno di profilare il codice per identificare il collo di bottiglia. Se il collo di bottiglia è l'analisi XML, mi dispiace, quella parte non può essere eseguita in parallelo. Potete per favore inviarmi via e-mail gli esatti file SVG che sono troppo lenti? Ogni volta che ho un po 'di tempo, posso indagare sulla performance.
Denilson Sá Maia,

Sì, ho provato a farlo da solo, e ho scoperto che l'analisi XML è la parte lenta, non decodificare le immagini. Detto questo, cElementTreedovrebbe essere più veloce. Ma forse anche qualcosa come Sax funziona meglio.
DanMan

@DanMan cElementTreeè probabilmente più veloce. Tuttavia, su Python 3.3, entrambi sono uguali . A un certo punto probabilmente aggiornerò quello script a Python 3.
Denilson Sá Maia,

5

Come ulteriore soluzione alternativa, puoi salvare come PDF, quindi aprire quel documento con Inkscape.

Deseleziona "incorpora immagini" e bingo, tutti i png / jpeg verranno sparsi nella tua home directory.

Disordinato, ma più veloce che scherzare con i dati: URL.


Dove hai trovato l'opzione "incorpora immagini"?
mik01aj,

1
Quando apri il documento PDF in inkscape, è nella finestra di dialogo successiva.
Nicholas Wilson,

Avevo un PDF dal quale ho cercato di estrarre un'immagine importandola in Inkscape. In questo caso, essendo in grado di fare questo su importazioni piuttosto che dopo l'importazione viene in ancora più a portata di mano.
user149408

Non sono sicuro, ma in questo modo qualsiasi profilo ICC incorporato sembra perdersi nel processo. Le immagini che ho estratto direttamente dallo SVG tramite quello script Python hanno profili ICC incorporati.
DanMan,

1

Miglioramento del php-script di @Johnny_Bit . La nuova versione dello script può usare svg con nuove righe. Estrae più immagini dal file svg e le salva in file png esterni. I file svg e png si trovano nella directory 'svg', ma puoi cambiarlo nella costante 'SVG_DIR'.

<?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>';
}

?>

0

Apri il tuo file in Inkscape e seleziona la bitmap che desideri esportare. Fai clic su File-> Esporta bitmap (Ctrl + Maiusc + E) e dovrebbe esportare solo la bitmap selezionata.


Non mi piace questa soluzione perché ricodificherà l'immagine. Preferirei una soluzione che estrae l'immagine nel suo formato originale.
Denilson Sá Maia,

1
Sì, sembra che Inkscape ricodificherà l'immagine, ma salva le immagini PNG per impostazione predefinita. Quindi presumo che la ricodifica sia almeno senza perdita di dati.
Chris,

1
Beh, non proprio. L'immagine incorporata potrebbe aver subito trasformazioni (ridimensionamento, rotazione ...), potrebbe essere stata ritagliata o anche qualcos'altro che non conosco. Inkscape esporterà sicuramente l'oggetto selezionato dopo aver applicato tutte queste trasformazioni, il che significa che questa soluzione non è esattamente senza perdite.
Denilson Sá Maia,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.