Bagaimana cara mengekstrak gambar yang disematkan dari file SVG?

26

Saya memiliki file SVG yang berisi setidaknya satu gambar JPG / PNG tertanam di dalamnya. Saya ingin mengekstrak gambar JPG / PNG dari file SVG itu dan menyimpannya di disk.

Saya menambahkan inkscapetag karena ini adalah program yang saya gunakan untuk mengedit file SVG, tetapi saya juga menerima solusi menggunakan alat lain.

Denilson Sa Maia
sumber
1
Jika tidak ada yang lain, Python mungkin bisa melakukannya dengan lem khusus menggunakan lxml dan PIL (atau yang setara).
Keith
@Keith, memang, saya baru saja menulis skrip Python untuk menyelesaikan pertanyaan ini. Ini menggunakan xml.etreeperpustakaan bawaan.
Denilson Sá Maia

Jawaban:

30

Solusi saya sendiri (atau ... solusi):

  1. Pilih gambar di Inkscape
  2. Buka built-in XML Editor( Shift+ Ctrl+ X)
  3. Pilih xlink:hrefatribut, yang akan berisi gambar sebagai data: URI
  4. Salin seluruh data:URI
  5. Rekatkan data:URI itu ke browser, dan simpan dari sana.

Atau, saya dapat membuka file SVG di editor teks apa pun, menemukan data:URI dan menyalinnya dari sana.

Meskipun solusi ini berhasil, agak rumit dan saya ingin belajar yang lebih baik.

Denilson Sa Maia
sumber
2
+1 - Saya mengekspor gambar 3,5 MB menggunakan metode ini yang butuh waktu tetapi bekerja. Entah bagaimana fungsi "Ekstrak Gambar" tidak berfungsi untuk saya.
Martin
Silakan lihat juga skrip Python baris perintah untuk tujuan ini.
Denilson Sá Maia
17

Ada solusi yang lebih baik sebagai gantinya:

pergi ke Extensions -> Images -> Extract Image..., di sana Anda dapat menyimpan gambar raster yang dipilih sebagai file. Namun ekstensi ini bekerja aneh dan entah bagaimana bekerja agak lambat (tapi sangat baik).

Catatan lain: ekstensi ini rumit dan mati secara diam-diam di berbagai gambar besar. Juga, dengan sejumlah besar gambar raster, ia dapat melonjak penggunaan memori inkscape ke tingkat yang mengerikan (seperti 3GB setelah hanya beberapa gambar diekstraksi).

Karena saya punya sekitar 20 file svg dengan masing-masing sekitar 70 gambar raster, masing-masing gambar berukuran setidaknya 1MB, saya membutuhkan solusi yang berbeda. Setelah pemeriksaan singkat menggunakan Denilson Sá tip saya membuat skrip php berikut, yang mengekstrak gambar dari 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);

Dengan cara ini saya bisa mendapatkan semua gambar yang saya inginkan, dan md5 menyelamatkan saya dari mendapatkan gambar yang diulang.

Saya yakin pasti ada cara lain yang jauh lebih sederhana, tetapi terserah inkscape devs untuk melakukannya dengan lebih baik.

Johnny_Bit
sumber
Catatan: Skrip Anda hanya mendukung satu data:URL per baris, dan tidak mendukung baris baru di dalam atribut href (inkscape menambahkannya untuk URL data, dan bahkan spesifikasi base64 mengamanatkan bahwa garis tidak boleh lebih dari 76 karakter ). Skrip yang bagus untuk peretasan cepat, tetapi tidak bekerja dengan semua jenis SVG.
Denilson Sá Maia
@Johnny_Bit +1 untuk penggunaan jumlah md5 untuk mencegah duplikasi file. Saya meningkatkan skrip Anda di bawah ini .
Ivan Z
bagus, maret 2019 dan bekerja easy grand dengan citra yang cukup besar. Dan laptop / ubuntu / inkscape yang cukup tua 0.48.4. Terima kasih!
gaoithe
9

Akhirnya, bertahun-tahun kemudian, saya telah menulis sebuah skrip untuk mengekstrak semua gambar dengan benar dari file SVG, menggunakan pustaka XML yang tepat untuk mem-parsing kode SVG.

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

Skrip ini ditulis untuk Python 2.7 tetapi harus cukup mudah untuk dikonversi ke Python 3. Bahkan lebih baik, sekitar 50 baris dapat dihapus setelah konversi ke Python 3.4, karena fitur baru yang diperkenalkan dalam versi itu.

Denilson Sa Maia
sumber
Terima kasih, karena berhasil. Tapi ini jauh lebih lambat daripada solusi PDF. Sudahkah Anda memikirkan pemrosesan paralel? Saat ini, skrip hanya menggunakan inti / utas CPU tunggal.
DanMan
@DanMan Sayangnya, menjadikannya paralel bukanlah solusi ajaib untuk mempercepat apa pun. Saya perlu profil kode untuk mengidentifikasi kemacetan. Jika bottlenecknya adalah parsing XML, maaf, bagian itu tidak bisa dilakukan secara paralel. Bisakah Anda mengirimi saya melalui email file SVG yang terlalu lambat? Setiap kali saya punya waktu, saya dapat menyelidiki kinerja.
Denilson Sá Maia
Ya, saya mencoba melakukannya sendiri, dan ternyata parsing XML adalah bagian yang lambat, bukan decoding gambar. Konon, cElementTreeseharusnya lebih cepat. Tapi mungkin sesuatu seperti Sax bekerja lebih baik juga.
DanMan
@DanMan cElementTreemungkin lebih cepat. Namun, pada Python 3.3, keduanya sama . Pada titik tertentu, saya kemungkinan akan memperbarui skrip itu ke Python 3.
Denilson Sá Maia
5

Sebagai solusi lain, Anda dapat menyimpan sebagai PDF, lalu buka dokumen itu dengan Inkscape.

Hapus centang "embed images", dan bingo, semua pngs / jpegs akan dimuntahkan ke direktori home Anda.

Berantakan, tetapi lebih cepat dari pada bermain-main dengan data: URL.

Nicholas Wilson
sumber
Di mana Anda menemukan opsi "embed images"?
mik01aj
1
Saat Anda membuka dokumen PDF di inkscape, ada di dialog berikutnya.
Nicholas Wilson
Saya memiliki PDF dari mana saya mencoba untuk mengekstraksi gambar dengan mengimpornya di Inkscape. Dalam hal ini, dapat melakukan ini pada impor daripada setelah impor bahkan lebih praktis.
user149408
Saya tidak yakin tetapi dengan cara ini, setiap profil ICC tertanam tampaknya hilang dalam proses. Gambar yang saya ekstrak langsung dari SVG melalui skrip Python itu memiliki profil ICC.
DanMan
1

Saya meningkatkan naskah-php dari @Johnny_Bit . Rilis skrip baru dapat menggunakan svg dengan baris baru. Ini mengekstrak beberapa gambar dari file svg dan menyimpannya dalam file png eksternal. File Svg dan png ada dalam direktori 'svg', tetapi Anda dapat mengubahnya dalam 'SVG_DIR' konstan.

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

Buka file Anda di Inkscape dan pilih bitmap yang ingin Anda ekspor. Klik File-> Ekspor Bitmap (Ctrl + Shift + E) dan itu harus mengekspor hanya bitmap yang dipilih.

Chris
sumber
Saya tidak suka solusi ini karena ini akan menyandikan ulang gambar. Saya lebih suka solusi yang mengekstrak gambar dalam format aslinya.
Denilson Sá Maia
1
Ya, sepertinya Inkscape menyandikan ulang gambar tetapi menyimpan gambar PNG secara default. Jadi saya mengasumsikan bahwa pengkodean ulang setidaknya lossless.
Chris
1
Yah, tidak juga. Gambar yang disematkan mungkin memiliki transformasi (penskalaan, rotasi ...), mungkin terpotong, atau bahkan sesuatu yang tidak saya sadari. Inkscape tentu akan mengekspor objek yang dipilih setelah menerapkan semua transformasi ini, yang berarti solusi ini tidak sepenuhnya lossless.
Denilson Sá Maia