2015-10-16 11 views
13

Tôi có một mảng có kích thước trung bình (ví dụ: 1500x3000) mà tôi muốn vẽ theo tỷ lệ vì nó là hình ảnh. Tuy nhiên, các vảy dọc và ngang rất khác nhau. Để đơn giản hóa, hãy nói rằng có một mét/hàng và 10/cột. Cốt truyện sau đó sẽ tạo ra một hình ảnh là c. 1500x30000. Tôi sử dụng mức độ kwarg cho tỷ lệ và khía cạnh = 1 để tránh biến dạng. Hoặc bằng cách sử dụng các cửa sổ vẽ (QT4) và imshow() hoặc bằng cách sử dụng savefig(), tôi không bao giờ thành công trong việc sản xuất hình ảnh ở quy mô và ở độ phân giải đầy đủ.Vẽ ở độ phân giải đầy đủ với matplotlib.pyplot, imshow() và savefig()?

tôi đã xem xét nhiều giải pháp được đề xuất như đã nêu trong here, here, hoặc herethere hoặc there trong trường hợp đó là một lỗi. Tôi đã thay đổi matplotlibrc của tôi và đặt nó trong ~/.config/matplotlib để cố gắng buộc các tùy chọn hiển thị/lưu trữ của tôi nhưng không có kết quả. Tôi cũng đã thử với pcolormesh() nhưng không thành công. Tôi sử dụng python 2,7 và matplotlib 1.3 từ repo của Ubuntu 14.04 và QT4Agg như một phụ trợ. Tôi đã thử TkAgg nhưng nó rất chậm và cho kết quả tương tự. Tôi có ấn tượng rằng trong trục x độ phân giải là đúng nhưng nó chắc chắn bị downsampled theo hướng thẳng đứng. Đây là một đoạn mã nên mô phỏng vấn đề của tôi.

import numpy as np 
import matplotlib.pyplot as plt 
import matplotlib.colors 

R, C = 1500, 3000 
DATA = np.random.random((R, C)) 
DATA[::2, :] *= -1 # make every other line negative 
Yi, Xi = 1, 10 # increment 
CMP = 'seismic' 
ImageFormat ='pdf' 
Name = 'Image' 


DataRange = (np.absolute(DATA)).max() # I want my data centred on 0 
EXTENT = [0, Xi*C, 0 ,Yi*R] 
NORM = matplotlib.colors.Normalize(vmin =-DataRange, vmax= DataRange, clip =True) 

for i in range(1,4): 
    Fig=plt.figure(figsize=(45, 10), dpi = 100*i, tight_layout=True) 
    Fig.suptitle(Name+str(i)+'00DPI') 
    ax = Fig.add_subplot(1, 1, 1) 
    Plot = ax.imshow(DATA, cmap=plt.get_cmap(CMP), norm = NORM, extent = EXTENT, aspect = 1, interpolation='none') 
    ax.set_xlabel('metres') 
    ax.set_ylabel('metres') 
    Fig.savefig(Name+str(i)+'00DPI.'+ImageFormat, format = ImageFormat, dpi = Fig.dpi) 
plt.close() 

Trong imshow(), suy = 'none' hoặc 'gần' hoặc 'bilinear' không thay đổi độ phân giải cho một số lý do mặc dù tôi nghĩ nó là vụ phải ít nhất là trong cửa sổ Qt4 nếu tôi làm chương trình() thay vì savefig(). Lưu ý rằng độ phân giải là giống nhau trong các hình được lưu bất cứ điều gì bạn thiết lập trong plt.figure (dpi =).

Tôi hết ý tưởng và với giới hạn hiểu biết của mình về cách mọi thứ hoạt động với hệ thống này. Bất kỳ trợ giúp là rất hoan nghênh.

Xin cảm ơn trước.

+1

Có lưu dưới dạng tùy chọn SVG không? 'plt.savefig (" test.svg ")' – Eric

+0

Tôi đã không nhận thấy một sự cải thiện tiết kiệm như svg về độ phân giải dọc. – Boorhin

+0

Tôi đã sửa đổi mã để hình ảnh sẽ luân phiên các giá trị dương và âm theo chiều dọc. Ý tưởng chính là nếu các hình ảnh được giải quyết đầy đủ, chúng tôi sẽ có thể phân biệt các sọc ngang màu xanh và đỏ – Boorhin

Trả lời

1

Chạy ví dụ của bạn, mọi thứ có vẻ tốt trong matplotlib sau khi thu phóng: bất kể độ phân giải, kết quả là như nhau và tôi thấy một pixel trên mỗi đơn vị trục. Ngoài ra, hãy thử với các mảng nhỏ hơn, các tệp pdf (hoặc các định dạng khác) hoạt động tốt.

Đây là giải thích của tôi: khi bạn đặt hình dpi, bạn đang đặt dpi của toàn bộ hình (không chỉ vùng dữ liệu). Trên hệ thống của tôi, kết quả này trong khu vực âm mưu chiếm khoảng 20% ​​của toàn bộ hình. Nếu bạn đặt 300 dpi và 10 chiều cao, bạn sẽ nhận được trục dữ liệu dọc tổng cộng 300x10x0.2 = 600 pixel, không đủ để đại diện cho 1500 điểm, điều này giải thích cho tôi lý do đầu ra phải được lấy lại. Lưu ý rằng việc giảm chiều rộng đôi khi tình cờ hoạt động vì nó thay đổi phần con số bị chiếm bởi ô dữ liệu.

Sau đó, bạn phải tăng dpi và cũng đặt nội suy = 'không' (nó không quan trọng nếu độ phân giải được đặt hoàn hảo, nhưng nó quan trọng nếu nó chỉ đủ gần). Ngoài ra, bạn có thể điều chỉnh vị trí và kích thước của ô để chiếm một phần lớn hơn của hình, nhưng quay lại cài đặt độ phân giải tối ưu, lý tưởng là bạn muốn có một số điểm ảnh trên trục là nhiều điểm dữ liệu của bạn một số loại nội suy phải xảy ra (suy nghĩ cách bạn có thể vẽ hai điểm trên ba pixel, hoặc ngược lại).

Tôi không biết nếu sau đây là cách tốt nhất để làm điều đó, có thể có phương pháp phù hợp hơn và tài sản trong matplotlib, nhưng tôi sẽ cố gắng một cái gì đó như thế này để tính toán dpi tối ưu:

vsize=ax.get_position().size[1] #fraction of figure occupied by axes 
axesdpi= int((Fig.get_size_inches()[1]*vsize)/R) #(or Yi*R according to what you want to do) 

Sau đó, mã của bạn (được giảm xuống vòng đầu tiên), trở thành:

import numpy as np 
import matplotlib.pyplot as plt 
import matplotlib.colors 

R, C = 1500, 3000 
DATA = np.random.random((R, C)) 
DATA[::2, :] *= -1 # make every other line negative 
Yi, Xi = 1, 10 # increment 
CMP = 'seismic' 
ImageFormat ='pdf' 
Name = 'Image' 


DataRange = (np.absolute(DATA)).max() # I want my data centred on 0 
EXTENT = [0, Xi*C, 0 ,Yi*R] 
NORM = matplotlib.colors.Normalize(vmin =-DataRange, vmax= DataRange, clip =True) 

for i in (1,): 
    print i 
    Fig=plt.figure(figsize=(45, 10), dpi = 100*i, tight_layout=True) 
    Fig.suptitle(Name+str(i)+'00DPI') 
    ax = Fig.add_subplot(1, 1, 1) 
    Plot = ax.imshow(DATA, cmap=plt.get_cmap(CMP), norm = NORM, extent = EXTENT, aspect = 1, interpolation='none') 
    ax.set_xlabel('metres') 
    ax.set_ylabel('metres') 
    vsize=ax.get_position().size[1] #fraction of figure occupied by axes 
    axesdpi= int((Fig.get_size_inches()[1]*vsize)/R) #(or Yi*R according to what you want to do) 
    Fig.savefig(Name+str(axesdpi)+'DPI.'+ImageFormat, format = ImageFormat, dpi = axesdpi) 
    #plt.close() 

Điều này phù hợp với tôi.

1

Thứ nhất, khi bạn đang lưu dưới dạng .pdf, bạn đang sử dụng ngầm chương trình phụ trợ pdf, mặc dù bạn có thể chỉ định các phần phụ trợ khác trong tùy chọn của mình. Điều này có nghĩa là hình ảnh của bạn được lưu ở định dạng vector và do đó dpi khá vô nghĩa. Trong bất kỳ độ phân giải nào, nếu tôi tải lên PDF của bạn trong một trình xem phong nha (tôi đã sử dụng inkscape, những thứ khác có sẵn), bạn có thể thấy rõ các sọc - Tôi thực sự thấy dễ dàng hơn nếu bạn đặt mọi hàng thứ hai về 0. Tất cả các tệp PDF được tạo đều chứa thông tin đầy đủ để tái tạo các sọc và do đó hầu như giống hệt nhau. Khi bạn chỉ định figsize=(45, 10), tất cả các tệp PDF được tạo có kích thước màn hình được đề xuất 45 inch x 10 inch.

Nếu tôi chỉ định png làm loại hình ảnh, tôi thấy sự khác biệt về kích thước tệp dựa trên thông số dpi, mà tôi nghĩ là những gì bạn đang mong đợi. Nếu bạn nhìn vào hình ảnh 100 dpi, nó có 4500000, hình ảnh 200 dpi có 18000000 pixel (gấp 4 lần) và hình ảnh 300 dpi có 40500000 (nhiều gấp 9 lần). Bạn sẽ nhận thấy rằng 4500000 == 1500 x 3000 tức là một pixel cho mỗi thành viên của mảng ban đầu của bạn. Sau đó, sau đó, các cài đặt dpi lớn hơn không giúp bạn có thêm bất kỳ định nghĩa nào - thay vào đó, các sọc của bạn rộng 2 hoặc 3 pixel thay vì 1.

I nghĩ rằng những gì bạn muốn làm là hiệu quả vẽ từng cột 10 lần, vì vậy bạn có được hình ảnh 1500 x 30000 pixel. Để làm điều này, sử dụng tất cả các mã của riêng bạn, bạn có thể sử dụng np.repeat để làm một cái gì đó như sau:

import numpy as np 
import matplotlib.pyplot as plt 
import matplotlib.colors 

R, C = 1500, 3000 
DATA = np.random.random((R, C)) 
DATA[::2, :] = 0 # make every other line plain white 
Yi, Xi = 1, 10 # increment 
DATA = np.repeat(DATA, Xi, axis=1) 
DATA = np.repeat(DATA, Yi) 

CMP = 'seismic' 
ImageFormat ='pdf' 
Name = 'Image' 


DataRange = (np.absolute(DATA)).max() # I want my data centred on 0 
EXTENT = [0, Xi*C, 0 ,Yi*R] 
NORM = matplotlib.colors.Normalize(vmin =-DataRange, vmax= DataRange, clip =True) 

for i in range(1,4): 
    Fig=plt.figure(figsize=(45, 10), dpi = 100*i, tight_layout=True) 
    Fig.suptitle(Name+str(i)+'00DPI') 
    ax = Fig.add_subplot(1, 1, 1) 
    Plot = ax.imshow(DATA, cmap=plt.get_cmap(CMP), norm = NORM, extent = EXTENT, aspect = 1, interpolation='none') 
    ax.set_xlabel('metres') 
    ax.set_ylabel('metres') 
    Fig.savefig(Name+str(i)+'00DPI.'+ImageFormat, format = ImageFormat, dpi = Fig.dpi) 
plt.close() 

Nên biết trước: giải pháp chuyên sâu một kỷ niệm này - có thể có những cách tốt hơn ra khỏi đó.Nếu bạn không cần phải đầu ra đồ họa vector của pdf, bạn có thể thay đổi biến ImageFormat của bạn để png


Nó tấn công tôi rằng điều khác mà bạn có thể quan tâm đến việc là để cho hình ảnh tỉ lệ thích hợp (tức là 20 lần rộng vì nó cao). Điều này bạn đã làm. Vì vậy, nếu bạn nhìn vào từng biểu diễn của một pixel trong pdf, chúng là hình chữ nhật (cao gấp 10 lần chiều cao của chúng), không vuông.

+0

Chỉ cần làm rõ, số gia tăng là thang đo theo mét. Dữ liệu được lấy mẫu mỗi mét theo chiều dọc và mỗi 10 mét theo chiều ngang. Vì vậy, tôi không thực sự muốn lặp lại các giá trị, tôi thích một nội suy (như một bộ lọc trung bình). Tuy nhiên tôi hiểu rằng mã của tôi tại thời điểm này không được thiết kế cho điều đó (tôi đã xóa phần đó để đơn giản). Cảm ơn những lời giải thích, tôi không biết chức năng np.repeat. Tôi cũng sẽ sử dụng một định dạng raster, nó có ý nghĩa hơn. Nó chỉ là tôi thích rendering của các phông chữ/trục trong pdf, sau đó tôi có thể chỉnh sửa chúng trong Inkscape để xuất bản. – Boorhin

Các vấn đề liên quan