PerfectVector
โดย Irene Kimอ่าน 4 นาที

Matplotlib SVG: เก็บกราฟและโลโก้เป็นพาธ

ส่งออกกราฟ Matplotlib เป็น SVG จัดวางพาธโลโก้แยกด้วย svgutils แล้วตรวจไฟล์ที่ส่งมอบว่ามีพิกเซลฝังอยู่หรือไม่ รวมถึงตำแหน่งและการเปลี่ยนฟอนต์

ในหน้านี้

หากต้องการให้โลโก้ในรูปจาก Matplotlib ยังคงเป็นเวกเตอร์ ให้ส่งออกกราฟเป็น SVG แล้วประกอบเข้ากับเรขาคณิต SVG ที่มีอยู่ของโลโก้ การวาง PNG ด้วย OffsetImage อาจได้ SVG ที่มีภาพราสเตอร์อยู่ร่วมกับเส้นกราฟเวกเตอร์ นามสกุลไฟล์อย่างเดียวไม่ได้บอกว่าส่วนใดเป็นพาธ

คู่มือนี้แยกสองงานออกจากกัน: Matplotlib วาดกราฟจากข้อมูล และ svgutils วางตราตกแต่งไว้ข้างกราฟ หากภาพต้นฉบับเป็นเวกเตอร์อยู่แล้ว ให้ใช้ไฟล์นั้น หากเหลือเพียงภาพประกอบราสเตอร์ที่เหมาะสม ให้สร้างเวกเตอร์ของภาพประกอบนั้นแยกต่างหาก เก็บเครื่องหมายเชิงตัวเลข แกน และป้ายข้อความไว้ในต้นทางของกราฟ

ขั้นตอนโดยย่อ

ส่งออกกราฟ ประกอบภาพที่แยกไว้ ตรวจ SVG ที่ได้ แล้วเปิดไฟล์เดียวกับที่จะส่งมอบอีกครั้ง เก็บสคริปต์สร้างกราฟและไฟล์ภาพต้นฉบับไว้ เพื่อสร้างรูปใหม่เมื่อแก้ข้อมูลครั้งถัดไป

เลือกสิ่งที่ควรอยู่ในต้นทางของกราฟ

โลโก้เป็นงานภาพ ส่วนขีดแบ่งบนแกนเป็นส่วนของกราฟ การรวมทั้งคู่เป็นภาพเดียวทำให้แก้ไขภายหลังได้ยากขึ้น

เนื้อหาในรูปต้นทางที่ควรใช้สิ่งที่ต้องตรวจ
เส้น จุดสัญลักษณ์ แกน และป้ายข้อมูลโค้ดสร้างกราฟ Matplotlibค่า ป้ายข้อความ และการตั้งค่ากราฟ
โลโก้หรือภาพประกอบเวกเตอร์ที่มีอยู่ไฟล์ SVG ต้นฉบับพาธ ขอบเขต และสิ่งที่ไฟล์ต้องอ้างอิง
ภาพถ่ายราสเตอร์หรือภาพจากกล้องจุลทรรศน์ภาพราสเตอร์ต้นฉบับความละเอียดที่ขนาดใช้งานสุดท้าย
ลวดลายตกแต่งง่าย ๆ ที่ไฟล์เวกเตอร์ต้นทางสูญหายผลการสร้างเวกเตอร์ที่ตรวจแยกแล้วความตรงของรูปร่างและบริเวณที่ไม่ต้องการ

ไม่จำเป็นต้องทำทั้งรูปเป็นเวกเตอร์หากบางส่วนเป็นภาพถ่าย ให้รักษาส่วนเหล่านั้นเป็นราสเตอร์ เป้าหมายคือเก็บต้นทางที่เหมาะสมสำหรับแต่ละส่วน แล้วตรวจผลลัพธ์ที่ประกอบแล้ว

เอกสารอ้างอิง savefig ของ Matplotlib อธิบายการเลือกรูปแบบขาออกอย่างชัดเจน ส่วนAPI ของ OffsetImage อธิบายตัวเก็บภาพ ไม่ใช่ตัวนำเข้าพาธ SVG สำหรับการวินิจฉัยระดับไฟล์ ดูวิธีค้นหาภาพราสเตอร์ภายใน SVG

ไฟล์ที่ส่งมอบมีอะไรอยู่ภายใน

เรารันตัวอย่างด้วย Python 3.12, Matplotlib 3.10.7 และ svgutils 0.3.4 ตราเป็นภาพต้นฉบับที่วาดด้วยสองพาธ ค่าทั้งสี่ในกราฟเป็นข้อมูลประกอบตัวอย่าง ไม่ใช่ผลการทดลองหรือเกณฑ์วัดประสิทธิภาพผลิตภัณฑ์

การเปรียบเทียบใช้กราฟเดียวกันในทั้งสองไฟล์ ด้านซ้ายแทรกตรา PNG ขนาด 32 × 32 ด้วย OffsetImage ด้านขวาประกอบพาธ SVG ของตราเข้ากับกราฟที่ส่งออก บริเวณขยายมาจากไฟล์ที่ส่งมอบจริงเหล่านั้น

ไฟล์ SVG จาก Matplotlib จริงพร้อมบริเวณตราที่ขยาย: ตรา PNG ที่ฝังอยู่ด้านซ้าย และพาธ SVG ของตราที่ประกอบเข้ามาด้านขวา
กราฟยังเป็นเวกเตอร์ในทั้งสองไฟล์ ในตัวอย่างนี้ แบบราสเตอร์มีองค์ประกอบ image หนึ่งตัว ส่วนแบบประกอบเพิ่มพาธภาพสองพาธและไม่มีองค์ประกอบ image

เมื่อนับองค์ประกอบ SVG ได้ผลดังนี้:

ไฟล์องค์ประกอบ imageองค์ประกอบ pathองค์ประกอบ text
กราฟอย่างเดียว0815
กราฟพร้อมตรา PNG1815
กราฟพร้อมพาธ SVG ของตรา01015

จำนวนเหล่านี้ใช้กับตัวอย่างขนาดเล็กนี้ จำนวนพาธไม่ใช่คะแนนคุณภาพ และการไม่มีองค์ประกอบ image ก็ไม่ได้พิสูจน์ว่าคุณสมบัติ SVG ทุกอย่างจะคงอยู่เมื่อผ่านแอปอื่น แต่ยืนยันได้ว่าตราในไฟล์ที่ประกอบนี้เป็นเรขาคณิต ไม่ใช่ PNG ที่ฝังอยู่

ส่งออกกราฟด้วยขอบเขตที่คาดการณ์ได้

ติดตั้งเวอร์ชันที่ใช้ในบทความนี้ในสภาพแวดล้อมของโครงการ:

python -m pip install matplotlib==3.10.7 svgutils==0.3.4

บันทึกโค้ดนี้เป็น plot.py แล้วรันในโฟลเดอร์ทำงานที่ว่าง:

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
 
plt.rcParams["svg.fonttype"] = "none"
 
fig = plt.figure(figsize=(6, 3), dpi=72)
ax = fig.add_axes([0.14, 0.22, 0.58, 0.65])
ax.plot([0, 1, 2, 3], [1, 2, 1.5, 3],
        marker="o", color="#173b40")
ax.set(xlabel="Example x", ylabel="Example y",
       title="Illustrative data only")
ax.spines[["top", "right"]].set_visible(False)
fig.savefig("plot.svg", format="svg")
plt.close(fig)

แกนใช้พื้นที่เพียงส่วนหนึ่งของรูป จึงมีที่ว่างสำหรับตราทางขวา ไฟล์ที่ส่งออกมี viewBox เป็น 0 0 432 216 และขนาดภายนอก 432pt คูณ 216pt

คงขอบเขตเหล่านี้ไว้ขณะวางภาพภายนอก การเพิ่ม bbox_inches="tight" จะเปลี่ยนขอบเขตที่บันทึก ตามที่เอกสาร savefig อธิบาย หากต้องการครอบภาพให้ชิด ให้ตรวจ viewBox ที่ส่งออก แล้วปรับพิกัดการประกอบหลังจากนั้น

การตั้ง svg.fonttype="none" เก็บป้ายตัวอย่างเป็นข้อความ เอกสารฟอนต์ของ Matplotlib อธิบายทางเลือกในการส่งออกข้อความและพาธ SVG ข้อความที่ยังแก้ไขได้ต้องมีฟอนต์ที่เหมาะสมในสภาพแวดล้อมปลายทาง หากต้องการให้รูปทรงตัวอักษรคงที่ ให้เลือกส่งออกเป็นพาธอย่างตั้งใจ และเก็บต้นทางกราฟไว้แก้ป้ายข้อความ คู่มือฟอนต์ SVG เปลี่ยน อธิบายข้อแลกเปลี่ยนนี้

ประกอบภาพโดยไม่แปลงเป็นราสเตอร์

บันทึกตราต้นฉบับนี้เป็น badge.svg ไว้ข้าง plot.svg:

<svg xmlns="http://www.w3.org/2000/svg"
     width="100" height="100" viewBox="0 0 100 100">
  <path fill="#173b40" fill-rule="evenodd"
        d="M30 5H70L95 30V70L70 95H30L5 70V30Z
           M34 17L17 34V66L34 83H66L83 66V34L66 17Z"/>
  <path fill="#28bfa3" d="M53 23L30 55H47L43 77L70 43H53Z"/>
</svg>

จากนั้นรันสคริปต์ประกอบนี้:

import svgutils.transform as sg
 
page = sg.SVGFigure("432pt", "216pt")
page.root.set("viewBox", "0 0 432 216")
 
chart = sg.fromfile("plot.svg").getroot()
badge = sg.fromfile("badge.svg").getroot()
badge.moveto(332, 76, scale_x=0.64)
 
page.append([chart, badge])
page.save("vector-logo.svg")

วิธีนี้ทำตามขั้นตอนโหลด แปลงพิกัด เพิ่ม และบันทึกในบทสอนประกอบรูปของ svgutils เราใช้ scale_x ซึ่งเป็นอาร์กิวเมนต์ที่ svgutils 0.3.4 รองรับ ส่วนบทสอนเก่าแสดงอาร์กิวเมนต์สเกลอีกแบบ

ภาพตราที่กว้าง 100 หน่วยกลายเป็นกว้าง 64 หน่วย มุมซ้ายบนอยู่ที่ (332, 76) ในระบบพิกัดของรูปกราฟ ขนาดภายนอกและ viewBox ที่ระบุชัดทำให้ตำแหน่งนี้ไม่กำกวมสำหรับตัวอย่างนี้ พิกัดเหล่านี้เป็นพิกัดหน้า ไม่ใช่พิกัดข้อมูล การย้ายจุดข้อมูลในกราฟจะไม่ย้ายตราตาม

สำหรับคำอธิบายที่ต้องผูกกับจุดข้อมูลที่เปลี่ยนตำแหน่ง ให้รักษาการแมปจากพิกัดกราฟไปยังหน้าที่ส่งออกไว้อย่างชัดเจน หรือใช้เครื่องมือประกอบที่รองรับความสัมพันธ์นี้ ตำแหน่งคงที่บนหน้าเหมาะกับโลโก้มุมภาพหรือแผงภาพประกอบแยก

ไฟล์ SVG ต้นฉบับอาจมีคุณสมบัตินอกเหนือจากพาธ SVG ที่ฝัง PNG อยู่แล้วจะยังเป็นเนื้อหาแบบผสมหลังประกอบ เมื่อนำไฟล์ซับซ้อนมารวมกัน ให้ตรวจ ID ที่อ้างอิง ฟอนต์ และแอสเซ็ตที่ลิงก์ภายนอกด้วย ตราในตัวอย่างนี้หลีกเลี่ยงสิ่งที่ต้องอ้างอิงเหล่านั้น

ตรวจไฟล์แล้วทดลองแก้ข้อมูล

เปิด vector-logo.svg ในเบราว์เซอร์หรือโปรแกรมแก้ไขที่ตั้งใจใช้ ตรวจช่องตรงกลางตรา ป้ายกราฟ และพื้นที่ว่างระหว่างทั้งสอง ตรวจไฟล์ทั้งที่ขนาดใช้งานจริงและขนาดขยาย

การตรวจ XML สั้น ๆ ยืนยันโครงสร้างได้โดยไม่อาศัยภาพตัวอย่าง:

import xml.etree.ElementTree as ET
 
root = ET.parse("vector-logo.svg").getroot()
ns = "{http://www.w3.org/2000/svg}"
for tag in ("image", "path", "text"):
    print(tag, len(root.findall(".//" + ns + tag)))

สำหรับตัวอย่างที่ให้ โค้ดพิมพ์ image 0, path 10 และ text 15 ส่วนแบบราสเตอร์ที่ใช้เปรียบเทียบพิมพ์ image 1 โดยยังคงพาธแปดพาธของกราฟไว้ ในแบบเปรียบเทียบนั้น การอ้างอิงภาพบรรจุข้อมูล PNG เป็นราสเตอร์ที่มีข้อมูลครบในตัว ไม่ใช่ไฟล์โลโก้ภายนอกที่หายไป

ลองเปลี่ยนค่า y สุดท้ายของข้อมูลตัวอย่างใน plot.py รันใหม่ แล้วรันขั้นตอนประกอบใหม่ กราฟควรเปลี่ยน ขณะที่ตรายังคงอยู่ในตำแหน่งหน้าที่กันพื้นที่ไว้ การทดสอบแก้ข้อมูลนี้ช่วยจับกระบวนการที่ดูถูกต้องครั้งเดียว แต่พึ่งไฟล์กราฟส่งออกเก่า

ตรวจขั้นตอนส่งมอบถัดไปแยกต่างหาก หากรูปจะเข้าไปอยู่ในเอกสาร กระบวนการ SVG ใน LaTeX อธิบายทางเลือกการส่งออกและป้ายข้อความ ภาพตัวอย่างในเบราว์เซอร์ที่ถูกต้องอย่างเดียวไม่ได้ยืนยันว่าผ่านข้อกำหนดวารสารหรือคงสภาพใน PDF สุดท้าย

สร้างกลับเฉพาะภาพที่จำเป็นต้องสร้าง

เมื่อมีต้นทางกราฟ ให้ส่งออกจากต้นทางนั้น การแปลงเวกเตอร์ไม่สามารถคืนข้อมูลตัวเลขหรือคำสั่งวาดกราฟจากภาพหน้าจอได้

PerfectVector เหมาะกับกรณีเฉพาะที่โลโก้ตกแต่งหรือภาพประกอบง่าย ๆ เหลือเพียงภาพราสเตอร์ ในกระบวนการกู้ภาพประกอบวิทยาศาสตร์ ให้ครอบเฉพาะภาพนั้น ดูตัวอย่างเวกเตอร์ แล้วตรวจช่องเปิด รูปทรงบาง และบริเวณพื้นหลังที่ไม่ต้องการ อย่ารวมแกน เครื่องหมายจากการวัด และป้ายข้อความในการไล่เส้น ประกอบ SVG ภาพที่ยอมรับแล้วเข้ากับกราฟจากต้นทาง แล้วตรวจไฟล์ที่ส่งมอบซ้ำ

ใช้ไฟล์เวกเตอร์ต้นฉบับเมื่อมี วาดสัญลักษณ์ง่าย ๆ ใหม่ด้วยมือหากการไล่เส้นทำรายละเอียดสำคัญหาย เก็บภาพถ่ายเป็นราสเตอร์ ภาพรวมการแปลงภาพเป็นเวกเตอร์ อธิบายสิ่งที่การไล่เส้นสร้างกลับได้ก่อนเลือกวิธีนี้

คำถามที่พบบ่อย

บันทึกรูป Matplotlib เป็น SVG แล้วโลโก้ PNG จะเป็นเวกเตอร์หรือไม่? ไม่ ในกระบวนการ OffsetImage ที่ทดสอบ กราฟส่งออกเป็นเรขาคณิตเวกเตอร์ แต่โลโก้ PNG ยังเป็นองค์ประกอบ image ภายใน SVG

วางโลโก้ SVG ด้วยพิกัดข้อมูลได้หรือไม่? การประกอบที่แสดงนี้ใช้พิกัดหน้าที่ส่งออก โลโก้ที่ผูกกับจุดข้อมูลต้องมีการแมปพิกัดชัดเจน หรือใช้เครื่องมือที่รักษาความสัมพันธ์นั้น

ทำไมป้ายข้อความกลายเป็นพาธ? การตั้งค่าฟอนต์ขาออก SVG ของ Matplotlib ควบคุมว่าจะส่งออกป้ายเป็นข้อความหรือพาธรูปอักษร เลือกการตั้งค่านี้ก่อนส่งออก และเก็บต้นทางกราฟไว้เปลี่ยนข้อความภายหลัง

แหล่งอ้างอิง

  1. Matplotlib — savefig — การส่งออก SVG อย่างชัดเจนและผลของกรอบครอบภาพแบบชิด
  2. Matplotlib — OffsetImage — พฤติกรรมตัวเก็บภาพที่ใช้เปรียบเทียบกับราสเตอร์
  3. Matplotlib — ฟอนต์ใน Matplotlib — ทางเลือกส่งออกข้อความ SVG และพาธรูปอักษร
  4. svgutils — การสร้างรูปคุณภาพสำหรับตีพิมพ์ — การส่งออกกราฟแยก การประกอบ SVG การแปลงพิกัด และการบันทึกรูปที่ประกอบแล้ว

ส่งออกกราฟจากโค้ดและเก็บภาพแยกไว้ หากเหลือเพียงลวดลายตกแต่งราสเตอร์ เตรียมภาพ SVG สำหรับประเมิน ตรวจช่องเปิดและขอบเขต แล้วประกอบกับกราฟและตรวจไฟล์เดียวกับที่จะส่งมอบว่ามีพิกเซล ตำแหน่ง หรือป้ายข้อความเปลี่ยนไปหรือไม่

เพิ่มเติมจากบล็อก

เริ่มต้นด้วย SVG ที่เรียบร้อยขึ้น
และแก้ไขได้ง่ายกว่า