วิธีการแยกภาพที่ฝังจากไฟล์ SVG


26

ฉันมีไฟล์ SVG ที่มีรูปภาพ JPG / PNG ฝังอยู่อย่างน้อยหนึ่งภาพ ฉันต้องการแยกรูปภาพ JPG / PNG จากไฟล์ SVG นั้นและบันทึกลงในดิสก์

ฉันกำลังเพิ่มinkscapeแท็กเนื่องจากเป็นโปรแกรมที่ฉันใช้ในการแก้ไขไฟล์ SVG แต่ฉันยังยอมรับวิธีแก้ปัญหาด้วยเครื่องมืออื่น ๆ


1
ถ้าไม่มีอะไรอย่างอื่น Python ก็สามารถทำได้ด้วยกาวที่กำหนดเองโดยใช้ lxml และ PIL (หรือเทียบเท่า)
Keith

@ Keith แน่นอนฉันเพิ่งเขียนสคริปต์ Pythonเพื่อแก้ปัญหานี้ มันใช้xml.etreeห้องสมุดในตัว
Denilson Sá Maia

คำตอบ:


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 MB โดยใช้วิธีนี้ซึ่งใช้เวลาสักครู่ แต่ใช้งานได้ ฟังก์ชั่น "ดึงภาพ" ไม่ทำงานสำหรับฉัน
มาร์ติน

โปรดดูสคริปต์ Python ที่บรรทัดคำสั่งเพื่อจุดประสงค์นี้
Denilson Sá Maia

17

มีทางออกที่ดีกว่าแทน:

ไปที่Extensions -> Images -> Extract Image...นั่นคุณสามารถบันทึกภาพแรสเตอร์ที่เลือกเป็นไฟล์ อย่างไรก็ตามส่วนขยายนี้ใช้งานได้แปลกและใช้งานได้ค่อนข้างช้า (แต่สมบูรณ์ดี)

หมายเหตุอื่น: ส่วนขยายนี้ยุ่งยากและตายอย่างเงียบ ๆ บนภาพขนาดใหญ่ที่แตกต่างกัน นอกจากนี้ด้วยภาพแรสเตอร์จำนวนมากมันสามารถขัดขวางการใช้หน่วยความจำของ inkscape ในระดับที่น่ากลัว (เช่น 3GB หลังจากเพียงดึงภาพเพียงไม่กี่ภาพ)

เนื่องจากฉันมีไฟล์ประมาณ 20 svg โดยมีรูปภาพแรสเตอร์ประมาณ 70 ภาพในแต่ละภาพแต่ละภาพมีขนาดอย่างน้อย 1MB ฉันจึงต้องการโซลูชันที่แตกต่างกัน หลังจากตรวจสอบสั้น ๆ โดยใช้เคล็ดลับ Denilsonฉันคิดค้นสคริปต์ 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 devs ที่จะทำได้ดีกว่า


หมายเหตุ: สคริปต์ของคุณรองรับdata:URL เดียวต่อบรรทัดเท่านั้นและไม่รองรับการขึ้นบรรทัดใหม่ภายในแอตทริบิวต์ href (inkscape เพิ่มพวกเขาสำหรับ URL ข้อมูลและข้อมูลจำเพาะ base64 ยังบังคับว่าบรรทัดไม่ควรยาวเกิน 76 ตัวอักษร ) สคริปต์ที่ดีสำหรับการแฮ็คอย่างรวดเร็ว แต่ไม่สามารถใช้กับ SVG ได้ทุกประเภท
Denilson Sá Maia

@Johnny_Bit +1 สำหรับการใช้ผลรวม md5 เพื่อป้องกันการทำสำเนาไฟล์ ฉัน imrove สคริปต์ของคุณด้านล่าง
Ivan Z

ดี, มีนาคม 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 ดียิ่งขึ้นสามารถลบได้ประมาณ 50 บรรทัดหลังจากการแปลงเป็น Python 3.4 เนื่องจากคุณลักษณะใหม่ที่แนะนำในเวอร์ชันนั้น


ขอบคุณเนื่องจากมันใช้งานได้ แต่มันช้ากว่าวิธีแก้ปัญหา PDF มาก คุณคิดเกี่ยวกับการประมวลผลแบบขนานหรือไม่ ตอนนี้สคริปต์ใช้ CPU แกน / เธรดเดียวเท่านั้น
DanMan

@DanMan แต่น่าเสียดายที่การทำให้มันขนานกันนั้นไม่ได้เป็นวิธีแก้ปัญหาที่วิเศษในการเพิ่มความเร็วอะไร ฉันต้องการโปรไฟล์รหัสเพื่อระบุคอขวด หากคอขวดคือการแยกวิเคราะห์ XML ฉันขอโทษส่วนที่ไม่สามารถทำแบบขนาน คุณช่วยส่งไฟล์ SVG ที่ช้าเกินไปให้ฉันทางอีเมลได้ไหม เมื่อใดก็ตามที่ฉันมีเวลาฉันอาจตรวจสอบประสิทธิภาพ
Denilson Sá Maia

ใช่ฉันพยายามทำเองและปรากฎว่าการแยกวิเคราะห์ XML เป็นส่วนที่ช้าไม่ถอดรหัสภาพ ที่กล่าวว่าcElementTreeควรจะเร็วขึ้น แต่บางทีสิ่งที่คล้ายกับแซ็กโซโฟนก็ใช้ได้ดีกว่า
DanMan

@DanMan cElementTreeน่าจะเร็วกว่า อย่างไรก็ตามใน Python 3.3 ทั้งสองจะเหมือนกัน เมื่อถึงจุดหนึ่งฉันจะอัปเดตสคริปต์เป็น Python 3
Denilson Sá Maia

5

ในฐานะที่เป็นวิธีแก้ปัญหาอื่นคุณสามารถบันทึกเป็น PDF แล้วเปิดเอกสารนั้นด้วย Inkscape

ยกเลิกการเลือก "ฝังภาพ" และ bingo pngs / jpegs ทั้งหมดจะถูกพ่นเข้าไปในโฮมไดเร็กตอรี่ของคุณ.

ยุ่ง แต่เร็วกว่าความสับสนเกี่ยวกับ data: URL


คุณพบว่าตัวเลือก "ฝังภาพ" อยู่ที่ไหน
mik01aj

1
เมื่อคุณเปิดเอกสาร PDF ใน inkscape เอกสารนั้นจะอยู่ในกล่องโต้ตอบถัดไป
Nicholas Wilson

ฉันมี PDF ที่ฉันพยายามดึงภาพโดยการนำเข้าใน Inkscape ในกรณีที่ความสามารถในการทำเช่นนี้ในการนำเข้ามากกว่าหลังจากที่นำเข้ามาในแม้จะมีประโยชน์มากขึ้น
user149408

ฉันไม่แน่ใจ แต่ด้วยวิธีนี้โปรไฟล์ 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 และเลือกบิตแมปที่คุณต้องการส่งออก คลิกไฟล์ -> ส่งออกบิตแมป (Ctrl + Shift + E) และควรส่งออกเฉพาะบิตแมปที่เลือก


ฉันไม่ชอบโซลูชันนี้เพราะจะเข้ารหัสรูปภาพอีกครั้ง ฉันต้องการโซลูชันที่แยกรูปภาพในรูปแบบดั้งเดิม
Denilson Sá Maia

1
ใช่ดูเหมือนว่า Inkscape จะเข้ารหัสรูปภาพอีกครั้ง แต่จะบันทึกรูปภาพ PNG ตามค่าเริ่มต้น ดังนั้นฉันจึงสันนิษฐานว่าการเข้ารหัสซ้ำอย่างน้อยก็ไม่สูญเสีย
Chris

1
ก็ไม่ได้จริงๆ ภาพที่ฝังอาจมีการแปลง (การปรับการหมุน…) อาจถูกตัดหรือแม้แต่อย่างอื่นที่ฉันไม่ทราบ Inkscape จะส่งออกวัตถุที่เลือกอย่างแน่นอนหลังจากใช้การแปลงเหล่านี้ทั้งหมดซึ่งหมายความว่าวิธีนี้จะไม่สูญเสียอย่างแน่นอน
Denilson Sá Maia
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.