¿Cómo extraer una imagen incrustada de un archivo SVG?

26

Tengo un archivo SVG que contiene al menos una imagen JPG / PNG incrustada dentro. Quiero extraer las imágenes JPG / PNG de ese archivo SVG y guardarlas en el disco.

Estoy agregando la inkscapeetiqueta, ya que es el programa que uso para editar archivos SVG, pero también acepto soluciones con otras herramientas.

Denilson Sá Maia
fuente
1
Por lo menos, Python probablemente podría hacerlo con un poco de pegamento personalizado usando lxml y PIL (o equivalente).
Keith
@Keith, de hecho, acabo de escribir un script de Python para resolver esta pregunta. Utiliza la xml.etreebiblioteca incorporada .
Denilson Sá Maia

Respuestas:

30

Mi propia solución (o ... solución):

  1. Seleccione la imagen en Inkscape
  2. Abra el incorporado XML Editor( Shift+ Ctrl+ X)
  3. Seleccione el xlink:hrefatributo, que contendrá la imagen como datos: URI
  4. Copie el data:URI completo
  5. Pegue ese data:URI en un navegador y guárdelo desde allí.

Alternativamente, puedo abrir el archivo SVG en cualquier editor de texto, ubicar el data:URI y copiarlo desde allí.

Aunque esta solución funciona, es un poco engorrosa y me encantaría aprender una mejor.

Denilson Sá Maia
fuente
2
+1: exporté una imagen de 3,5 MB con este método, que tardó un tiempo pero funcionó. De alguna manera, la función "Extraer imagen" no funcionó para mí.
Martin
Consulte también un script Python de línea de comandos para este propósito.
Denilson Sá Maia
17

Hay una mejor solución en su lugar:

vaya a Extensions -> Images -> Extract Image..., allí puede guardar la imagen ráster seleccionada como un archivo. Sin embargo, esta extensión funciona de manera extraña y de alguna manera funciona bastante lento (pero perfectamente bien).

Otra nota: esta extensión es engorrosa y muere en silencio en varias imágenes grandes. Además, con una gran cantidad de imágenes ráster, puede aumentar el uso de memoria de Inkscape a niveles horrendos (como 3GB después de solo un puñado de imágenes extraídas).

Debido a que tengo alrededor de 20 archivos svg con aproximadamente 70 imágenes ráster en cada uno, cada imagen de al menos 1 MB de tamaño, necesitaba una solución diferente. Después de una breve comprobación con el consejo de Denilson Sá , ideé el siguiente script php, que extrae imágenes de los archivos 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);

De esta forma puedo obtener todas las imágenes que quiero, y md5 me evita obtener imágenes repetidas.

Apuesto a que debe haber otra forma que sea mucho más simple, pero depende de los desarrolladores de Inkscape hacerlo mejor.

Johnny_Bit
fuente
Nota: Su secuencia de comandos solo admite una única data:URL por línea, y no admite nuevas líneas dentro del atributo href (inkscape las agrega para las URL de datos, y la especificación base64 incluso exige que las líneas no tengan más de 76 caracteres ). Buen script para un hack rápido, pero no funciona con todo tipo de SVG.
Denilson Sá Maia
@Johnny_Bit +1 para el uso de md5 sum para evitar la duplicación de archivos. Mejoro tu guión a continuación .
Ivan Z
bueno, marzo de 2019 y funcionó fácilmente con una imagen razonablemente grande. Y bastante viejo portátil / ubuntu / inkscape 0.48.4. ¡Gracias!
Gaoithe
9

Finalmente, años después, escribí un script para extraer correctamente todas las imágenes de un archivo SVG, usando una biblioteca XML adecuada para analizar el código SVG.

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

Este script está escrito para Python 2.7, pero debería ser bastante fácil de convertir a Python 3. Aún mejor, se pueden eliminar alrededor de 50 líneas después de la conversión a Python 3.4, debido a las nuevas características introducidas en esa versión.

Denilson Sá Maia
fuente
Gracias, ya que funciona. Pero es mucho más lento que la solución de PDF. ¿Has pensado en el procesamiento paralelo? En este momento, el script solo usa un núcleo / hilo de CPU.
DanMan
@DanMan Desafortunadamente, hacerlo en paralelo no es una solución mágica para acelerar nada. Necesitaría perfilar el código para identificar el cuello de botella. Si el cuello de botella es el análisis XML, lo siento, esa parte no se puede hacer en paralelo. ¿Pueden enviarme por correo electrónico los archivos SVG exactos que son demasiado lentos? Siempre que tenga algo de tiempo, puedo investigar el rendimiento.
Denilson Sá Maia
Sí, intenté hacerlo yo mismo, y resultó que el análisis XML es la parte lenta, no la decodificación de las imágenes. Dicho esto, cElementTreese supone que es más rápido. Pero tal vez algo como Sax también funciona mejor.
DanMan
@DanMan cElementTreees probablemente más rápido. Sin embargo, en Python 3.3, ambos son lo mismo . En algún momento probablemente actualizaré ese script a Python 3.
Denilson Sá Maia
5

Como otra solución alternativa, puede guardar como PDF y luego abrir ese documento con Inkscape.

Desmarque "incrustar imágenes" y bingo, todos los png / jpegs se incluirán en su directorio de inicio.

Desordenado, pero más rápido que jugar con los datos: URL.

Nicholas Wilson
fuente
¿Dónde encontraste esa opción de "insertar imágenes"?
mik01aj
1
Cuando abre el documento PDF en Inkscape, aparece en el siguiente cuadro de diálogo.
Nicholas Wilson
Tenía un PDF del que intenté extraer una imagen importándola en Inkscape. En ese caso, ser capaz de hacer esto en la importación en lugar de después de la importación resulta aún más útil.
user149408
No estoy seguro, pero de esta manera los perfiles ICC integrados parecen perderse en el proceso. Las imágenes que extraje directamente del SVG a través de ese script Python tienen perfiles ICC incrustados.
DanMan
1

Mejoro el php-script de @Johnny_Bit . La nueva versión del script puede usar svg con nuevas líneas. Extrae múltiples imágenes del archivo svg y las guarda en archivos png externos. Los archivos SVG y PNG están en el directorio 'svg', pero puede cambiarlo en 'SVG_DIR' constante.

<?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
fuente
0

Abra su archivo en Inkscape y seleccione el mapa de bits que desea exportar. Haga clic en Archivo-> Exportar mapa de bits (Ctrl + Shift + E) y debería exportar solo el mapa de bits seleccionado.

Chris
fuente
No me gusta esta solución porque volverá a codificar la imagen. Preferiría una solución que extraiga la imagen en su formato original.
Denilson Sá Maia
1
Sí, parece que Inkscape vuelve a codificar la imagen pero guarda las imágenes PNG de forma predeterminada. Por lo tanto, supongo que la nueva codificación es al menos sin pérdidas.
Chris
1
Bueno en realidad no. La imagen incrustada podría haber tenido transformaciones (escala, rotación ...), podría haber sido recortada, o incluso algo más que no sé. Inkscape ciertamente exportará el objeto seleccionado después de aplicar todas estas transformaciones, lo que significa que esta solución no es exactamente sin pérdidas.
Denilson Sá Maia