2013-02-12 35 views
18

Câu hỏi này đã được hỏi theo cách tương tự here nhưng câu trả lời là cách trên đầu của tôi (tôi siêu mới với python và phát triển web) vì vậy tôi hy vọng có một cách đơn giản hơn hoặc có thể giải thích khác đi.Tự động phục vụ một hình ảnh matplotlib lên web bằng cách sử dụng python

Tôi đang cố gắng tạo một hình ảnh bằng cách sử dụng matplotlib và phân phối nó mà không cần phải ghi tệp vào máy chủ trước. Mã của tôi có lẽ là loại ngớ ngẩn, nhưng nó đi như thế này:

import cgi 
import matplotlib.pyplot as pyplot 
import cStringIO #I think I will need this but not sure how to use 

...a bunch of matplotlib stuff happens.... 
pyplot.savefig('test.png') 

print "Content-type: text/html\n" 
print """<html><body> 
...a bunch of text and html here... 
<img src="test.png"></img> 
...more text and html... 
</body></html> 
""" 

Tôi nghĩ rằng thay vì làm pyplot.savefig ('test.png'), nên anh phải để tạo ra một đối tượng cstringIO và sau đó làm điều gì đó như thế này:

mybuffer=cStringIO.StringIO() 
pyplot.savefig(mybuffer, format="png") 

Nhưng tôi khá lạc mất từ ​​đó. Tất cả các ví dụ tôi đã thấy (ví dụ: http://lost-theory.org/python/dynamicimg.html) liên quan đến việc thực hiện một cái gì đó như

print "Content-type: image/png\n" 

và tôi không hiểu cách tích hợp với HTML mà tôi đã xuất.

+0

Khi bạn xuất blob từ 'imageout.py', sau đó trong HTML bạn có thể đặt nguồn hình ảnh thành tập lệnh py không? Trong php bạn sẽ làm điều gì đó dọc theo những dòng đó. – ninMonkey

+1

Kiểm tra http://stackoverflow.com/questions/1207190/embedding-base64-images –

Trả lời

18

Bạn nên

  • ghi đầu tiên đến một đối tượng cStringIO
  • sau đó viết tiêu đề HTTP
  • sau đó viết nội dung của cStringIO stdout

Do đó, nếu xảy ra lỗi trong số savefig, bạn vẫn có thể trả về một thứ khác, thậm chí một tiêu đề khác. Một số lỗi sẽ không được nhận dạng trước đó, ví dụ: một số sự cố với văn bản, kích thước hình ảnh quá lớn, v.v.

Bạn cần phải nói savefig nơi để viết đầu ra. Bạn có thể làm:

format = "png" 
sio = cStringIO.StringIO() 
pyplot.savefig(sio, format=format) 
print "Content-Type: image/%s\n" % format 
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) # Needed this on windows, IIS 
sys.stdout.write(sio.getvalue()) 

Nếu bạn muốn nhúng hình ảnh vào HTML:

print "Content-Type: text/html\n" 
print """<html><body> 
...a bunch of text and html here... 
<img src="data:image/png;base64,%s"/> 
...more text and html... 
</body></html>""" % sio.getvalue().encode("base64").strip() 
+0

Hoạt động hoàn hảo, cảm ơn! –

+0

bạn được chào đón ... –

2

Trừ khi tôi xấu miscomprehend câu hỏi của bạn, tất cả các bạn cần làm là cd đến vị trí của hình ảnh và chạy: python -m SimpleHTTPServer 8000 &

Sau đó, mở trình duyệt và gõ http://localhost:8000/ trong thanh địa chỉ URL.

+1

Không phải những gì tôi đang tìm kiếm nhưng bạn đã nhắc tôi viết lại để rõ ràng, cảm ơn! –

5

Câu hỏi đầu tiên của tôi là: Hình ảnh có thay đổi thường xuyên không? Bạn có muốn giữ những cái cũ hơn không? Nếu đó là một điều thời gian thực, thì nhiệm vụ tối ưu hóa của bạn là hợp lý. Nếu không, lợi ích từ việc tạo ra hình ảnh trên bay là không đáng kể.

Mã này vì nó đứng sẽ yêu cầu 2 yêu cầu:

  1. để có được nguồn html bạn đã có và
  2. để có được những hình ảnh thực tế

Có lẽ cách đơn giản nhất (giữ yêu cầu web ở mức tối thiểu) là nhận xét của @Alex L, cho phép bạn làm điều đó trong một yêu cầu duy nhất, bằng cách xây dựng HTML với hình ảnh được nhúng trong đó.

Mã của bạn sẽ là một cái gì đó như:

# Build your matplotlib image in a iostring here 
# ...... 
# 

# Initialise the base64 string 
# 
imgStr = "data:image/png;base64," 

imgStr += base64.b64encode(mybuffer) 

print "Content-type: text/html\n" 
print """<html><body> 
# ...a bunch of text and html here... 
    <img src="%s"></img> 
#...more text and html... 
    </body></html> 
""" % imgStr 

Mã này có thể sẽ không làm việc ra khỏi hộp, nhưng cho thấy các ý tưởng. Lưu ý rằng đây là một ý tưởng tồi nói chung nếu hình ảnh của bạn không thực sự thay đổi quá thường xuyên hoặc tạo ra nó mất một thời gian dài, bởi vì nó sẽ được tạo ra mỗi lần.

Một cách khác là tạo html gốc. Tải nó sẽ kích hoạt một yêu cầu cho "test.png". Bạn có thể phân phối riêng biệt, hoặc thông qua giải pháp phát trực tuyến đệm mà bạn đã đề cập hoặc từ một tệp tĩnh.

Cá nhân, tôi muốn kết hợp với một giải pháp tách rời: tạo hình ảnh bằng một quy trình khác (đảm bảo luôn có sẵn hình ảnh) và sử dụng một thứ rất nhẹ để tạo và phân phát HTML.

HTH,

6

Những câu trả lời trên là một chút lạc hậu - đây là những gì làm việc cho tôi trên Python3 + để lấy raw bytes của dữ liệu hình.

import matplotlib.pyplot as plt 
from io import BytesIO 
fig = plt.figure() 
plt.plot(range(10)) 
figdata = BytesIO() 
fig.savefig(figdata, format='png') 

Như đã đề cập trong các câu trả lời khác, bạn cần đặt tiêu đề 'Kiểu nội dung' thành 'hình ảnh/png' và ghi các byte.

Tùy thuộc vào những gì bạn đang sử dụng làm máy chủ web của mình, mã có thể thay đổi. Tôi sử dụng Tornado như máy chủ web của tôi và các mã để làm điều đó là:

self.set_header('Content-Type', 'image/png') 
self.write(figdata.getvalue()) 
+0

Tôi gặp sự cố khi nhận được độ dài nội dung chính xác cho bộ đệm. Khi tôi tải xuống một hình ảnh qua ajax, tôi luôn gặp lỗi 'ERR_CONTENT_LENGTH_MISMATCH', đã thử' sys.getsizeof (figdata) 'và' figdata .__ sizeof __() '. Cuối cùng tôi không chỉ định chiều dài nội dung. – TomTom101

+1

@ TomTom101 - Bạn đang sử dụng thư viện nào cho máy chủ HTTP của mình? Bạn có thể muốn 'len (figdata.getvalue())' (bạn đã nhận được kích thước của đối tượng BytesIO, không phải luồng dữ liệu thực tế) –

+0

Sử dụng nginx, Gunicorn và Falcon. Tôi sẽ thử đề xuất của bạn – TomTom101

1

những gì làm việc cho tôi với python3 là:

buf = io.BytesIO() 
plt.savefig(buf, format='png') 
image_base64 = base64.b64encode(buf.getvalue()).decode('utf-8').replace('\n', '') 
buf.close() 
Các vấn đề liên quan