我正在尋找一種在 python 中進行像素操作的有效方法。目標是制作一個充當嵌入式系統虛擬桌面的 python 腳本。我已經有一個可用的版本,但是顯示單幀需要一秒鐘以上(太長)。
每秒重繪 顯示 5 次會很棒。
怎么運行的:
- 有一個帶有微控制器和顯示幕(128x64px,黑白像素)的電子設備。
- 有一臺 PC 通過 RS-485 連接到它。
- 微控制器中有一個資料緩沖區,代表每個像素。讓我們稱之為 diplay_buffer。
- PC 上的 Python 腳本從微控制器下載 diplay_buffer。
- Python 腳本根據來自 diplay_buffer 的資料創建影像。(這個我需要優化)
display_buffer 是一個 1024 位元組的陣列。微控制器對其進行準備,然后在真實顯示幕上顯示其內容。我需要使用 python 腳本在 PC 螢屏上顯示真實顯示的虛擬副本。
它是如何顯示的:
display_buffer 中的單個位表示單個像素。顯示幕有 128x64 像素。diplay_buffer 中的每個位元組代表垂直方向的 8 個像素。前 128 個位元組代表第一行像素(位元組中有 64px / 8 個像素 = 8 行)。
我使用 python TK 和函式 img.put() 來插入像素。如果位為1,我插入黑色像素,如果位為0,則插入白色。這是非常無效的。Meybe有比PhotoImage不同的類,具有更好的像素能力?
我附上帶有示例 diplay_buffer 的最小代碼。運行腳本時,您將看到幀和執行時間。
Meybe會有人幫助嘗試優化它嗎?你能告訴我更快的顯示像素的方法嗎?
登德代爾
從 uC 下載的示例框架
和代碼(您可以輕松運行它)
#this script displays value from uC display buffer in a python screen
from tkinter import Tk, Canvas, PhotoImage, mainloop
from math import sin
import time
WIDTH, HEIGHT = 128, 64
ROWS = 8
#some code from tutorial... check what it does:
window = Tk()
canvas = Canvas(window, width=WIDTH, height=HEIGHT, bg="#ffffff")
canvas.pack()
img = PhotoImage(width=WIDTH, height=HEIGHT)
canvas.create_image((WIDTH/2, HEIGHT/2), image=img, state="normal")
#this is sample screen from uC. It is normally periodically read from uC on runtime to refresh screen view.
diplay_buffer =bytes([16, 16, 16, 16, 16, 0, 16, 16, 16, 16, 16, 0, 16, 16, 16, 16, 16, 0, 16, 16, 16, 16, 16, 0, 16, 16, 16, 16, 16, 0, 16, 16, 16, 16, 16, 0, 16, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 130, 254, 130, 0, 0, 254, 32, 16, 8, 254, 0, 254, 144, 144, 144, 128, 0, 124, 130, 130, 130, 124, 0, 0, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 0, 16, 16, 16, 16, 16, 0, 16, 16, 16, 16, 16, 0, 16, 16, 16, 16, 16, 0, 16, 16, 16, 16, 16, 0, 16, 16, 16, 16, 16, 0, 16, 16, 16, 16, 16, 0, 16, 16, 16, 16, 16, 0, 0, 0, 18, 42, 42, 42, 36, 0, 28, 34, 34, 34, 28, 0, 0, 16, 126, 144, 64, 0, 32, 32, 252, 34, 36, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 130, 252, 128, 0, 4, 42, 42, 30, 2, 0, 62, 16, 32, 32, 30, 0, 0, 0, 0, 0, 0, 0, 0, 66, 254, 2, 0, 0, 130, 132, 136, 144, 224, 0, 0, 0, 0, 0, 0, 0, 78, 146, 146, 146, 98, 0, 124, 138, 146, 162, 124, 0, 78, 146, 146, 146, 98, 0, 78, 146, 146, 146, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 254, 16, 16, 16, 254, 0, 28, 42, 42, 42, 24, 0, 0, 130, 254, 2, 0, 0, 0, 130, 254, 2, 0, 0, 28, 34, 34, 34, 28, 0, 0, 0, 0, 0, 0, 0, 254, 144, 144, 144, 128, 0, 62, 16, 32, 32, 16, 0, 0, 34, 190, 2, 0, 0, 28, 42, 42, 42, 24, 0, 62, 16, 32, 32, 30, 0, 28, 34, 34, 20, 254, 0, 0, 0, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 124, 130, 130, 130, 68, 0, 4, 42, 42, 30, 2, 0, 62, 16, 32, 32, 30, 0, 0, 0, 0, 0, 0, 0, 50, 9, 9, 9, 62, 0, 28, 34, 34, 34, 28, 0, 60, 2, 2, 4, 62, 0, 0, 0, 0, 0, 0, 0, 28, 34, 34, 34, 28, 0, 63, 24, 36, 36, 24, 0, 32, 32, 252, 34, 36, 0, 0, 34, 190, 2, 0, 0, 62, 32, 30, 32, 30, 0, 0, 34, 190, 2, 0, 0, 34, 38, 42, 50, 34, 0, 28, 42, 42, 42, 24, 0, 64, 128, 154, 144, 96, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 146, 146, 146, 108, 0, 4, 42, 42, 30, 2, 0, 28, 34, 34, 34, 20, 0, 254, 8, 20, 34, 0, 0, 0, 0])
def get_normalized_bit(value, bit_index):
return (value >> bit_index) & 1
time_start = time.time()
#first pixels are drawn invisible (some kind of frame in python) so set an offset:
x_offset = 2
y_offset = 2
x=x_offset
y=y_offset
#display all uC pixels (single screen frame):
byteIndex=0
for j in range(ROWS): #multiple rows
for i in range(WIDTH): #row
for n in range(8): #byte
if get_normalized_bit(diplay_buffer[byteIndex], 7-n):
img.put("black", (x,y n))
else:
img.put("white", (x,y n))
x =1
byteIndex =1
x=x_offset
y =7
time_stop = time.time()
print("Refresh time: ", str(time_stop - time_start), "seconds")
mainloop()
uj5u.com熱心網友回復:
我并沒有真正使用Tkinter,但我讀過使用put()將單個像素寫入影像非常慢。因此,我修改了您的代碼,改為將像素放入 Numpy 陣列,然后使用PIL將其轉換為PhotoImage.
在我的 Mac 上,將位元組緩沖區轉換為 aPhotoImage大約需要 1 毫秒。如果將三個for回圈包裝成 Numba-jitted 函式,它可能會快 10-100 倍,但它似乎不值得,因為它可能已經足夠快了。
#!/usr/bin/env python3
import numpy as np
from tkinter import *
from PIL import Image, ImageTk
# INSERT YOUR variable display_buffer here <<<
# Make a Numpy array of uint8, that will become
# ... our PIL Image that will become...
# ... a PhotoImage
WIDTH, HEIGHT, ROWS = 128, 64, 8
na = np.zeros((HEIGHT,WIDTH), np.uint8)
idx = 0
x = y = 0
for j in range(ROWS):
for i in range(WIDTH):
b = display_buffer[idx]
for n in range(8):
na[y n, x] = (1 - ((b >> (7-n)) & 1)) * 255
idx = 1
x = 1
x = 0
y = 7
# Make Numpy array into PIL Image
PILImage = Image.fromarray(na)
border = 10
root = Tk()
canvas = Canvas(root, width = 2*border WIDTH, height = 2*border HEIGHT)
canvas.pack()
# Make PIL Image into PhotoImage
img = ImageTk.PhotoImage(PILImage)
canvas.create_image(border, border, anchor=NW, image=img)
root.mainloop()
另外,我不知道你的串行線有多快,但傳輸 1024 位元組可能需要一些時間,所以你可以考慮啟動第二個執行緒從你的串行中重復讀取 1024 位元組并將它們填充到Queue主行程中從給get()他們。
此外,您可以完全避免使用Tkinter,而只需像這樣使用OpenCV imshow():
#!/usr/bin/env python3
import numpy as np
import cv2
# INSERT YOUR display_buffer here <<<
# Make a Numpy array of uint8, that will be displayed
WIDTH, HEIGHT, ROWS = 128, 64, 8
na = np.zeros((HEIGHT,WIDTH), np.uint8)
idx = 0
x = y = 0
for j in range(ROWS):
for i in range(WIDTH):
b = display_buffer[idx]
for n in range(8):
na[y n, x] = (1 - ((b >> (7-n)) & 1)) * 255
idx = 1
x = 1
x = 0
y = 7
while True:
# Display image
cv2.imshow("Virtual Console", na)
# Wait for user to press "q" to quit
if cv2.waitKey(1) & 0xFF == ord('q'):
break
我決定嘗試一下,Numba提取 128x64 幀的時間下降到 68 微秒。請注意,Python 必須第一次編譯,所以我進行了一次熱身運行以包含編譯,然后測量了第二次運行:
#!/usr/bin/env python3
import numba as nb
import numpy as np
from tkinter import *
from PIL import Image, ImageTk
import time
# Make a Numpy array of uint8, that will become
# ... our PIL Image that will become...
# ... a PhotoImage
WIDTH, HEIGHT, ROWS = 128, 64, 8
na = np.zeros((HEIGHT,WIDTH), np.uint8)
@nb.njit()
def extract(na,display_buffer):
idx = 0
x = y = 0
for j in range(ROWS):
for i in range(WIDTH):
b = display_buffer[idx]
for n in range(8):
na[y n, x] = (1 - ((b >> (7-n)) & 1)) * 255
idx = 1
x = 1
x = 0
y = 7
return na
# Following is first run which includes compilation time
warmup = extract(na, display_buffer)
# Only time the second run
start = time.time()
na = extract(na, display_buffer)
# Make Numpy array into PIL Image
PILImage = Image.fromarray(na)
elapsed = (time.time()-start)*1000
print(f'Total time: {elapsed} ms') # Reports 0.068 ms
border = 10
root = Tk()
canvas = Canvas(root, width = 2*border WIDTH, height = 2*border HEIGHT)
canvas.pack()
# Make PIL Image into PhotoImage
img = ImageTk.PhotoImage(PILImage)
canvas.create_image(border, border, anchor=NW, image=img)
root.mainloop()
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/420797.html
標籤:
