Closed fesselbach closed 8 years ago
@Saij might know best how to implement that.
What is your current code ? It sounds slow, even for Python, to take 2 seconds for 18432 function calls.
Am 03.06.16 um 17:15 schrieb Henner Zeller:
@Saij https://github.com/Saij might know best how to implement that.
What is your current code ? It sounds slow, even for Python, to take 2 seconds for 18432 function calls.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/hzeller/rpi-rgb-led-matrix/issues/152#issuecomment-223607269, or mute the thread https://github.com/notifications/unsubscribe/AS0HoyeJWMs30weIg4CWCCG9tFQ6Htndks5qIEUYgaJpZM4ItbUj.
Hello,
its the code example from the bottom of this site:
https://github.com/hzeller/rpi-rgb-led-matrix/issues/91
Here is my complete code. Sent/Receive by UDP and load needs ca. 0.1s, painting by SetImage ca. 2..3s
from PIL import Image from time import sleep from rgbmatrix import RGBMatrix from rgbmatrix import graphics from StringIO import StringIO import socket, struct, fcntl, os, psutil
def left(str, amount): return str[:amount] def right(str, amount): return str[-amount:] def mid(str, start, amount): return str[start:start+amount]
print("open") port=3307 width=192 height=32 chain=width/32 parallel=3
size=55350 #size of bmp 192 x 96 addenum=3 #size of additional commands
rem_adr="" command=""
matrix = RGBMatrix(height,chain,parallel)
host="0.0.0.0" font = graphics.Font() font.LoadFont("/home/pi/rpi-rgb-led-matrix-master/fonts/7x13.bdf") p=psutil.Process(os.getpid()) p.nice(16) print("start")
def get_ip(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try:
s.connect(("10.255.255.255", 0))
IP = s.getsockname()[0]
except:
IP = "0.0.0.0"
finally:
s.close()
return IP
def SetImage(dat,mx):
img = Image.open(StringIO(dat)) (w,h) = img.size
pix = img.load()
osc = mx.CreateFrameCanvas() for x in xrange(w): for y in xrange(h): (r,g,b) = pix[x,y] osc.SetPixel(x,y,r,g,b) sleep(0.015) osc = mx.SwapOnVSync(osc)
def recv_data(): length=size+addenum buffer= "" global rem_adr,command while len(buffer)<length: data,adr = sock.recvfrom(length - len(buffer)) buffer=buffer+data rem_adr=adr command=left(buffer,3) return right(buffer,size)
def idle(): matrix.Clear() matrix.Fill(10,10,10) ya=10 ye=40
col=graphics.Color(255,0,0) graphics.DrawLine(matrix, 160, ya, 160, ye, col) graphics.DrawLine(matrix, 161, ya, 161, ye, col) graphics.DrawLine(matrix, 162, ya, 162, ye, col)
col=graphics.Color(0,255,0) graphics.DrawLine(matrix, 170, ya, 170, ye, col) graphics.DrawLine(matrix, 171, ya, 171, ye, col) graphics.DrawLine(matrix, 172, ya, 172, ye, col)
col=graphics.Color(0,0,255) graphics.DrawLine(matrix, 180, ya, 180, ye, col) graphics.DrawLine(matrix, 181, ya, 181, ye, col) graphics.DrawLine(matrix, 182, ya, 182, ye, col)
col=graphics.Color(200,0,200)
graphics.DrawLine(matrix,0,0,192,0, col) graphics.DrawLine(matrix,0,95,192,95, col) graphics.DrawLine(matrix,0,1,0,95, col) graphics.DrawLine(matrix,191,1,191,95, col)
textColor = graphics.Color(255, 255, 0) graphics.DrawText(matrix,font,10,20,textColor,get_ip())
def init(): global sock sock=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((host, port)) sock.settimeout(10)
print("init") init() idle() print("work")
while True:
try:
try:
buf=recv_data()
print("BUF:",len(buf)," CMD:",command)
if command=="<I>": # re-init socket
command=""
buf=""
matrix.Clear()
init()
elif command=="<X>": # close program
command=""
buf=""
matrix.Clear()
sleep(.2)
os._exit(0)
elif command=="<R>": # reboot raspi
command=""
buf=""
matrix.Clear()
sleep(.2)
result=os.system("sudo reboot")
sleep(.2)
os._exit(0)
elif command=="<?>": # answer for searching by ip
command=""
buf=""
sock.sendto("<OK>",rem_adr)
elif command=="IMG" and len(buf)==size: # image found in buf
try:
SetImage(buf,matrix)
sock.sendto("<OK>",rem_adr)
command=""
buf=""
except:
sock.sendto("<EE>",rem_adr)
command=""
buf=""
else:
command=""
buf=""
sleep(0.1)
except socket.timeout:
command=""
buf=""
idle()
except KeyboardInterrupt: matrix.Clear() sleep(.2) os._exit(0)
(unrelated) Why are you creating a frame canvas in each SetImage() ? You'll quickly run out of memory.
The idea with the swapping FrameCanvases is to create a second frame-canvas once somewhere, and then swap with something like
osc = mx.SwapOnVSync(osc)
What comes back is always the previous canvas, so you can use one canvas to SetPixel() while the other is displayed.
Am 03.06.16 um 17:50 schrieb Henner Zeller:
(unrelated) Why are you creating a frame canvas in each SetImage() ? You'll quickly run out of memory.
I have also tried by direct painting without swapping. the result is near the same (by time).
Sorry can't help you with this. I have to take a look on how to implement a good interface to PIL. I don't have any knowledge about this class.
@Saij this looks like how the Adafruit library does SetImage() https://github.com/adafruit/rpi-rgb-led-matrix/blob/master/rgbmatrix.cc#L143 .. looks like there is a lot of cruft in the Python Image class.
@fesselbach I would suggest that you just send a plain preprocessed RGB buffer over to your program, as the Image class requires a lot computing (because it tries to be 'generic') which is probably what is slow.
@fesselbach the swapping is a good practice, but what you are doing is to create a new framebuffer every time with CreateFrameCanvas(), fill it once and give it to the matrix, but never re-use it. So I suggest to create it once (in main() ?) and assign it to some variable (in your current structure, this needs to be a global variable), and then do what you do now.
So do
osc = mx.CreateFrameCanvas()
once when main starts, and remove this call in SetImage()
; everything else stays the same. Then the SwapOnVSync()
call will do what it should. If assigning it in main creates a global variable also accessible in SetImage()
in Python (which I don't know if it does), then this should work.
Hello,
could be possible (with python) directy copy pre-prepared data inside the matrix object? like this:
data= socket_read matrix=data
what structures and types are inside the matrix?
thanks ...
No, there is no buffer of some kind to copy. The matrix only provides a SetPixel() abstraction in both C++ and Python. So even in C++, you would do the nested loop with setting the values one after another.
(The inner data structure in C++ is decomposing the color values and optimizing the raw memory layout to the way it is outputting things to the GPIO, so it is not simply a buffer of RGB values. It is not exposed in the interface in any way)
Hi,
In my mac-application i create a raw block of image data with a simple structure of only: r-g-b-r-g-b... etc. This job needs a time for making and sending (UDP) ca. 50ms. I do this with Xojo (formerly RealBasic).
I have made the smallest code for raspi's python that I can do ... but its not significant faster - ca. 2,5 sec for drawing a picture with 192 x 96 pixels ... not good :-(((
def SetImage(dat): pos=0 w=192 h=96 rgb=map(ord,dat) global osc,matrix
for y in xrange(h): for x in xrange(w): osc.SetPixel(x,y,rgb[pos],rgb[pos+1],rgb[pos+2]) p +=3
osc=matrix.SwapOnVSync(osc)
Cool, thanks for the minimal test. So it indeed looks that whatever the Python conversion is doing is doing something slow. This should be orders of magnitude faster if you write it in C++ (and also might only be 50 lines of code).
The Python wrapper wraps C++ and mabye it is more expensive ?
If you really want to go the Python way, it might be easier to make Python talk to a C API. I hear it is possible relatively easily, but I have never done Python so don't know for sure. Way easier than C++ though.
We got that covered: there is a C API for the LED matrix ( in include/led-matrix-c.h ). Maybe that would help ?
Hmmm, I suspect that there must be something like that, but I do not understand more than 10% of what it says. like this?
http://intermediate-and-advanced-software-carpentry.readthedocs.io/en/latest/c++-wrapping.html
It's not like that I was too lazy to learn that, but I need it soon and do not have much time.
That "SetImage" missing really is a nasty trap ...
Well, it is free software, if you need something, you really should consider adding it yourself and send a pull request. Nothing gets done if we complain about it, just by fixing it. So, after your time-critical thing is done, you should spend the two hours needed learning about interfacing Python with C/C++ and provide a SetBuffer() function. I can't do it because I am not too interested in Python and Saij is busy with other stuff.
In the meantime, you could write a simple UDP server like this in C++ https://gist.github.com/hzeller/9ece7d7150c6d28ed1825eee85b5d243
One solution (that I might work on later when my matrix arrives from China) is to have the C++ library deal with the control of the matrix and receive updated bitmaps via zeromq ( http://zguide.zeromq.org/ ) thus I can deal with interfacing with the data sources and producing the text for my matrix in language of my choice and still have the c++ library do all of the intensive matrix control.
I was in a similar boat, wanting to use Adafruit's older "rgbmatrix.cc" Python library with the current RGBMatrix library, and I documented my solution on Adafruit's Forum: https://forums.adafruit.com/viewtopic.php?f=50&t=98006
The update was fairly simple -- their library needed the third "parallel" argument added, and a function involving "SetWriteCycles" had to be remarked out. It's still early to tell if this is all that was required, but it allowed me to get my older Python scripts using SetImage (and all the other wonderful PIL functions, such as ImageFont) to work with the current hzeller library.
I have make a project with a chain of three modules some weeks ago by using the adafruit hat and lib with python. This works nice and really speedy. Now I'am beginning a new project with the passive three chain adapter and 3 x 3 modules and there is no function "SetImage"
I have tried the pure python solution with loop, but it is to slow (ca. 2s for 192 x 96)
I'am not familar with C or C++ at the raspi ... i can python only and now I'am breaking down ...
(I have tried to create a "pull request", but I have not understand, whats happend, if I click this button - please move this post if its needed)