PerfectVector
Bởi Irene Kim11 phút đọc

SVG Matplotlib: Giữ đồ thị và logo dưới dạng đường dẫn

Xuất đồ thị Matplotlib thành SVG, ghép các đường dẫn logo riêng bằng svgutils và kiểm tra tệp bàn giao để tìm pixel nhúng, sai vị trí hoặc thay đổi phông chữ.

Trên trang này

Để giữ logo ở dạng vector trong hình Matplotlib, hãy xuất đồ thị thành SVG rồi ghép với hình học SVG sẵn có của logo. Chèn PNG bằng OffsetImage có thể tạo ra SVG chứa ảnh raster bên cạnh các đường vector của đồ thị. Chỉ nhìn phần mở rộng không thể biết phần nào là đường dẫn.

Hướng dẫn này tách riêng hai công việc: Matplotlib vẽ đồ thị từ dữ liệu, còn svgutils đặt một huy hiệu trang trí bên cạnh. Nếu hình gốc đã là vector, hãy dùng nó. Nếu chỉ còn một hình minh họa raster phù hợp, hãy khôi phục riêng hình đó. Giữ các dấu biểu diễn số liệu, trục và nhãn trong mã nguồn vẽ đồ thị.

Quy trình ngắn gọn

Xuất đồ thị, ghép hình riêng, kiểm tra SVG kết quả, rồi mở lại đúng tệp bạn sẽ bàn giao. Giữ tập lệnh vẽ đồ thị và bản gốc của hình để lần cập nhật dữ liệu tiếp theo có thể tạo lại hình.

Chọn nội dung thuộc về mã nguồn đồ thị

Logo là hình thiết kế; vạch chia trục là một phần của đồ thị. Gộp cả hai thành một ảnh khiến việc sửa đổi sau này khó hơn.

Nội dung hìnhNguồn nên dùngĐiều cần kiểm tra
Đường, ký hiệu điểm, trục và nhãn dữ liệuMã vẽ đồ thị MatplotlibGiá trị, nhãn và các lựa chọn khi vẽ
Logo hoặc hình minh họa vector sẵn cóBản gốc SVG của nóĐường dẫn, giới hạn và các thành phần phụ thuộc
Ảnh chụp raster hoặc ô ảnh hiển viẢnh raster gốcĐộ phân giải ở kích thước cuối
Họa tiết trang trí đơn giản đã mất nguồn vectorBản khôi phục ứng viên được kiểm tra riêngĐộ chính xác của hình dạng và các vùng không mong muốn

Không cần hình hoàn toàn bằng vector nếu một số ô là ảnh chụp. Hãy giữ chúng ở dạng raster. Mục tiêu là giữ nguồn phù hợp cho từng thành phần, rồi kiểm tra kết quả đã ghép.

Tài liệu savefig của Matplotlib mô tả cách chọn rõ định dạng đầu ra. API OffsetImage mô tả vùng chứa ảnh, chứ không phải công cụ nhập đường dẫn SVG. Để chẩn đoán ở cấp tệp, xem cách tìm ảnh raster bên trong SVG.

Các tệp bàn giao chứa gì

Chúng tôi chạy ví dụ với Python 3.12, Matplotlib 3.10.7 và svgutils 0.3.4. Huy hiệu là hình tự tạo gồm hai đường dẫn. Bốn giá trị trên đồ thị là dữ liệu minh họa, không phải thí nghiệm hay phép đo hiệu năng sản phẩm.

Phép đối chiếu dùng cùng một đồ thị trong cả hai tệp. Bên trái, huy hiệu PNG 32 × 32 được chèn bằng OffsetImage. Bên phải, các đường dẫn SVG của nó được ghép với đồ thị đã xuất. Những vùng phóng to được lấy từ các tệp bàn giao thực tế đó.

Các tệp SVG Matplotlib thực tế với vùng huy hiệu phóng to: huy hiệu PNG nhúng bên trái và các đường dẫn huy hiệu SVG đã ghép bên phải
Đồ thị vẫn là vector trong cả hai tệp. Ở mẫu thử này, bản raster chứa một phần tử image; bản ghép thêm hai đường dẫn hình và không chứa phần tử image nào.

Đếm các phần tử SVG cho kết quả sau:

TệpPhần tử imagePhần tử pathPhần tử text
Chỉ đồ thị0815
Đồ thị có huy hiệu PNG1815
Đồ thị có đường dẫn huy hiệu SVG01015

Các số đếm này áp dụng cho mẫu thử nhỏ này. Số đường dẫn không phải điểm chất lượng, và không có phần tử image cũng không chứng minh mọi tính năng SVG sẽ được giữ khi đưa vào ứng dụng khác. Tuy nhiên, nó xác nhận huy hiệu trong tệp ghép này là hình học chứ không phải PNG nhúng.

Xuất đồ thị với giới hạn xác định được

Cài các phiên bản dùng ở đây trong một môi trường dự án:

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

Lưu đoạn này thành plot.py và chạy trong một thư mục làm việc trống:

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)

Các trục chỉ chiếm một phần của hình, chừa chỗ bên phải cho huy hiệu. Bản xuất này có viewBox là 0 0 432 216, với kích thước ngoài 432pt × 216pt.

Giữ nguyên các giới hạn này khi đặt hình bên ngoài. Thêm bbox_inches="tight" sẽ thay đổi giới hạn được lưu, như tài liệu savefig giải thích. Nếu cần cắt sát, hãy kiểm tra viewBox đã xuất rồi cập nhật tọa độ ghép sau đó.

Thiết lập svg.fonttype="none" giữ các nhãn trong ví dụ dưới dạng văn bản. Tài liệu phông chữ của Matplotlib mô tả các lựa chọn xuất văn bản SVG và đường dẫn. Văn bản còn chỉnh sửa được vẫn cần phông chữ phù hợp trong môi trường tiếp nhận. Nếu cần hình chữ cố định, hãy chủ động chọn xuất đường dẫn và giữ mã nguồn đồ thị để sửa nhãn. Hướng dẫn thay đổi phông chữ SVG trình bày sự đánh đổi đó.

Ghép hình mà không raster hóa

Lưu huy hiệu tự tạo này thành badge.svg bên cạnh 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>

Sau đó chạy tập lệnh ghép này:

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")

Cách này làm theo quy trình tải, biến đổi, thêm và lưu trong hướng dẫn ghép hình svgutils. Chúng tôi dùng scale_x, tham số được svgutils 0.3.4 hỗ trợ; hướng dẫn cũ dùng một tham số tỷ lệ khác.

Hình huy hiệu 100 đơn vị trở thành rộng 64 đơn vị. Góc trên bên trái nằm tại (332, 76) trong hệ tọa độ của đồ thị. Kích thước ngoài và viewBox được chỉ định rõ giúp vị trí trong mẫu thử này không mơ hồ. Đây là tọa độ trang, không phải tọa độ dữ liệu: di chuyển một điểm dữ liệu trong đồ thị sẽ không kéo huy hiệu theo.

Với chú thích gắn vào một điểm dữ liệu thay đổi, hãy duy trì ánh xạ có chủ đích từ tọa độ đồ thị sang trang xuất, hoặc dùng công cụ ghép có cung cấp mối liên kết đó. Vị trí trang cố định phù hợp với logo ở góc hoặc một ô minh họa riêng.

Bản gốc SVG có thể chứa nhiều tính năng ngoài đường dẫn. SVG vốn nhúng PNG vẫn có nội dung hỗn hợp sau khi ghép. Khi kết hợp những tệp phức tạp hơn, cũng cần kiểm tra ID được tham chiếu, phông chữ và tài nguyên liên kết bên ngoài. Huy hiệu trong ví dụ này tránh những thành phần phụ thuộc đó.

Kiểm tra tệp, rồi thử một lần sửa đổi

Mở vector-logo.svg trong trình duyệt hoặc trình chỉnh sửa bạn định dùng. Kiểm tra lỗ giữa huy hiệu, nhãn đồ thị và khoảng trống giữa chúng. Xem tệp ở kích thước bạn thực sự sẽ dùng, cũng như khi phóng to.

Một phép kiểm tra XML nhỏ có thể xác nhận cấu trúc mà không cần dựa vào bản xem trước:

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)))

Với mẫu thử được cung cấp, đoạn này in image 0, path 10 và text 15. Bản đối chiếu raster in image 1, trong khi giữ tám đường dẫn của đồ thị. Trong bản đó, tham chiếu ảnh chứa dữ liệu PNG; đó là nội dung raster tự chứa, không phải tệp logo bên ngoài bị thiếu.

Bây giờ hãy đổi giá trị y minh họa cuối trong plot.py, chạy lại nó rồi chạy lại bước ghép. Đồ thị cần thay đổi trong khi huy hiệu vẫn ở vị trí trang đã dành sẵn. Kiểm tra sửa đổi này giúp phát hiện quy trình trông đúng một lần nhưng lại phụ thuộc vào bản xuất đồ thị cũ.

Kiểm tra riêng bước bàn giao tiếp theo. Nếu hình sẽ được đưa vào tài liệu, quy trình SVG cho LaTeX giải thích các lựa chọn xuất và nhãn. Chỉ xem trước thành công trong trình duyệt không xác nhận hình đáp ứng quy định tạp chí hay được giữ nguyên trong PDF cuối cùng.

Chỉ khôi phục phần hình cần khôi phục

Khi mã nguồn đồ thị còn, hãy xuất từ đó. Vector hóa không thể phục hồi dữ liệu số hay lệnh vẽ đồ thị từ ảnh chụp màn hình.

PerfectVector phù hợp với trường hợp hẹp hơn: logo trang trí hoặc hình minh họa đơn giản chỉ còn ở dạng ảnh raster. Với quy trình khôi phục hình khoa học, hãy cắt riêng phần hình đó, xem trước bản vector ứng viên và kiểm tra lỗ, hình mảnh cùng các vùng nền không mong muốn. Đừng đưa trục, dấu đo và nhãn vào bước dò nét. Ghép SVG hình đã chấp nhận với đồ thị gốc, rồi lặp lại các phép kiểm tra tệp bàn giao.

Dùng bản gốc vector khi bạn có nó. Vẽ lại thủ công một biểu tượng đơn giản nếu dò nét làm mất chi tiết quan trọng. Giữ các ô ảnh chụp ở dạng raster. Tổng quan về vector hóa ảnh giải thích những gì dò nét có thể khôi phục trước khi bạn chọn cách này.

Câu hỏi thường gặp

Lưu hình Matplotlib thành SVG có biến logo PNG thành vector không? Không. Trong quy trình OffsetImage đã kiểm tra, đồ thị xuất thành hình học vector còn logo PNG vẫn là phần tử image bên trong SVG.

Tôi có thể đặt logo SVG bằng tọa độ dữ liệu không? Bước ghép minh họa ở đây dùng tọa độ trang đã xuất. Logo gắn với một điểm dữ liệu cần ánh xạ tọa độ rõ ràng hoặc công cụ duy trì mối liên kết đó.

Tại sao nhãn của tôi trở thành đường dẫn? Thiết lập xuất phông chữ SVG của Matplotlib quyết định nhãn được xuất thành văn bản hay đường dẫn của ký tự. Chọn thiết lập trước khi xuất, và giữ mã nguồn đồ thị để sửa nội dung sau này.

Nguồn tham khảo

  1. Matplotlib — savefig — Xuất SVG với định dạng chỉ định rõ và tác động của giới hạn cắt sát.
  2. Matplotlib — OffsetImage — Hoạt động của vùng chứa ảnh dùng trong bản đối chiếu raster.
  3. Matplotlib — Phông chữ trong Matplotlib — Các lựa chọn xuất văn bản SVG và đường dẫn ký tự.
  4. svgutils — Tạo hình đạt chất lượng xuất bản — Xuất riêng đồ thị, ghép SVG, biến đổi và lưu hình đã lắp ghép.

Xuất đồ thị từ mã và giữ phần hình riêng. Nếu chỉ còn họa tiết trang trí raster, hãy chuẩn bị bản SVG ứng viên cho hình, kiểm tra lỗ và ranh giới, rồi ghép với đồ thị và kiểm tra đúng tệp bàn giao để tìm pixel, sai vị trí và thay đổi nhãn.

Thêm từ blog

Bắt đầu với SVG gọn sạch hơn
và dễ chỉnh sửa hơn