Как извлечь встроенное изображение из файла SVG?

26

У меня есть файл SVG, который содержит по крайней мере одно встроенное изображение JPG / PNG внутри. Я хочу извлечь изображения JPG / PNG из этого файла SVG и сохранить их на диске.

Я добавляю inkscapeтег, так как это программа, которую я использую для редактирования файлов SVG, но я также принимаю решения, используя другие инструменты.

Денилсон Са Майя
источник
1
Если не что иное, Python, вероятно, мог бы сделать это с помощью какого-то специального клея, используя lxml и PIL (или эквивалентный).
Кит
@ Кейт, действительно, я только что написал скрипт на Python, чтобы решить этот вопрос. Он использует встроенную xml.etreeбиблиотеку.
Денилсон Са Майя

Ответы:

30

Мое собственное решение (или ... обходной путь):

  1. Выберите изображение в Inkscape
  2. Откройте встроенный XML Editor( Shift+ Ctrl+ X)
  3. Выберите xlink:hrefатрибут, который будет содержать изображение в качестве данных: URI
  4. Скопируйте весь data:URI
  5. Вставьте этот data:URI в браузер и сохраните его оттуда.

Кроме того, я могу открыть файл SVG в любом текстовом редакторе, найти data:URI и скопировать его оттуда.

Хотя это решение работает, оно довольно громоздко, и я хотел бы узнать лучшее.

Денилсон Са Майя
источник
2
+1 - я экспортировал изображение размером 3,5 МБ, используя этот метод, который занял некоторое время, но работал. Каким-то образом функция «Извлечь изображение» не работает для меня.
Мартин
Пожалуйста, смотрите также сценарий Python для командной строки .
Денилсон Са
17

Вместо этого есть лучшее решение:

перейдите к Extensions -> Images -> Extract Image..., там вы можете сохранить выбранное растровое изображение в виде файла. Однако это расширение работает странно и как-то работает довольно медленно (но совершенно хорошо).

Еще одно замечание: это расширение громоздко и тихо умирает на разных больших изображениях. Кроме того, с большим количеством растровых изображений это может увеличить использование памяти inkscape до ужасных уровней (например, 3 ГБ после нескольких извлеченных изображений).

Поскольку у меня есть около 20 SVG-файлов с около 70 растровыми изображениями в каждом, каждое размером не менее 1 МБ, мне нужно было другое решение. После небольшой проверки с помощью Denilson S tip я разработал следующий php-скрипт, который извлекает изображения из файлов 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);

Таким образом, я могу получить все нужные изображения, а md5 избавляет меня от повторных изображений.

Могу поспорить, что должен быть другой способ, который намного проще, но разработчики Inkscape должны сделать это лучше.

Johnny_Bit
источник
Примечание. Ваш сценарий поддерживает только один data:URL-адрес на строку и не поддерживает переводы строки в атрибуте href (inkscape добавляет их для URL-адресов данных, а спецификация base64 даже предписывает, что строки не должны быть длиннее 76 символов ). Хороший скрипт для быстрого взлома, но он не работает со всеми видами SVG.
Денилсон Са Майя
@Johnny_Bit +1 за использование суммы md5 для предотвращения дублирования файлов. Я улучшил ваш сценарий ниже .
Иван З
хорошо, март 2019 года и работал легко и грандиозно с достаточно большим имиджем. И довольно старый ноутбук / Ubuntu / Inkscape 0.48.4. Благодарность!
gaoithe
9

Наконец, спустя годы, я написал скрипт для правильного извлечения всех изображений из файла SVG, используя правильную библиотеку XML для анализа кода SVG.

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

Этот сценарий написан для Python 2.7, но его довольно легко конвертировать в Python 3. Более того, после преобразования в Python 3.4 можно удалить около 50 строк благодаря новым функциям, появившимся в этой версии.

Денилсон Са Майя
источник
Спасибо, так как это работает. Но это намного медленнее, чем обходной путь PDF. Вы думали о параллельной обработке? Прямо сейчас сценарий использует только одно ядро ​​/ поток процессора.
DanMan
@DanMan К сожалению, параллельная работа не является волшебным решением для ускорения чего-либо. Мне нужно профилировать код, чтобы определить узкое место. Если узким местом является разбор XML, извините, эта часть не может быть выполнена параллельно. Не могли бы вы выслать мне по электронной почте точные файлы SVG, которые являются слишком медленными? Всякий раз, когда у меня есть время, я могу исследовать представление.
Денилсон Са Майя
Да, я попытался сделать это сам, и оказалось, что синтаксический анализ XML - это медленная часть, а не декодирование изображений. Тем не менее, cElementTreeдолжен быть быстрее. Но, может быть, что-то вроде Sax тоже работает лучше.
DanMan
@ DanMan cElementTreeскорее всего быстрее. Тем не менее, в Python 3.3 оба будут одинаковыми . В какой-то момент я, скорее всего, обновлю этот скрипт до Python 3.
Денилсон Са Майя
5

В качестве еще одного обходного пути, вы можете сохранить как PDF, а затем открыть этот документ с помощью Inkscape.

Снимите флажок «вставлять изображения» и, бинго, все pngs / jpegs будут выброшены в ваш домашний каталог.

Грязно, но быстрее, чем дурачиться с данными: URL.

Николас Уилсон
источник
Где вы нашли эту опцию «вставлять изображения»?
mik01aj
1
Когда вы открываете документ PDF в inkscape, он появляется в следующем диалоговом окне.
Николас Уилсон
У меня был PDF, из которого я пытался извлечь изображение, импортировав его в Inkscape. В этом случае возможность сделать это при импорте, а не после импорта становится еще более удобной.
user149408 25.11.16
Я не уверен, но таким образом любые встроенные профили ICC, кажется, теряются в процессе. Изображения, которые я извлек прямо из SVG через этот скрипт Python, имеют встроенные профили ICC.
DanMan
1

Я улучшаю php-скрипт @Johnny_Bit . Новая версия скрипта может использовать svg с новыми строками. Он извлекает несколько изображений из SVG-файла и сохраняет их во внешних PNG-файлах. Файлы svg и png находятся в каталоге 'svg', но вы можете изменить его в константе '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

Откройте свой файл в Inkscape и выберите растровое изображение, которое вы хотите экспортировать. Нажмите File-> Export Bitmap (Ctrl + Shift + E), и он должен экспортировать только выбранное растровое изображение.

Крис
источник
Мне не нравится это решение, потому что оно перекодирует изображение. Я бы предпочел решение, которое извлекает изображение в исходном формате.
Денилсон Са Майя
1
Да, кажется, что Inkscape перекодирует изображение, но по умолчанию сохраняет изображения в формате PNG. Итак, я предполагаю, что перекодирование по крайней мере без потерь.
Крис
1
Ну не совсем. Внедренное изображение могло иметь преобразования (масштабирование, вращение ...), могло быть обрезано или даже что-то еще, чего я не знаю. Inkscape, безусловно, экспортирует выбранный объект после применения всех этих преобразований, что означает, что это решение не совсем без потерь.
Денилсон Са Майя