Merge branch 'master' of repos.ctdo.de:ctdo/dfi-led-matrix
This commit is contained in:
commit
12c6ef61a0
4 changed files with 166 additions and 136 deletions
|
@ -18,7 +18,6 @@ class MatrixSender(object):
|
||||||
global threadrunning
|
global threadrunning
|
||||||
threadrunning=False
|
threadrunning=False
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, udphost, udpport, img_size=(160,48), bitsperpixel=1):
|
def __init__(self, udphost, udpport, img_size=(160,48), bitsperpixel=1):
|
||||||
|
|
||||||
self._udphost = udphost
|
self._udphost = udphost
|
||||||
|
@ -31,58 +30,21 @@ class MatrixSender(object):
|
||||||
|
|
||||||
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
|
||||||
|
def send(self, image,invert=False):
|
||||||
|
|
||||||
def _list2byte(self, l):
|
|
||||||
byte = 0
|
|
||||||
i = 0
|
|
||||||
for i in range(8):
|
|
||||||
byte += 2**(7-i) if l[i] else 0
|
|
||||||
return byte
|
|
||||||
|
|
||||||
def _array2packet(self, a):
|
|
||||||
return [self._list2byte(a[i*8:i*8+8]) for i in range(int(len(a)/8))]
|
|
||||||
|
|
||||||
def _intToBitlist(self,number): #convert integer number to list of bits, exampe: 1-> [0,1], 2->[1,0]
|
|
||||||
bitlistvaryinglength=[x for x in "{0:b}".format(number)]
|
|
||||||
bitlist=np.zeros(self.bitsperpixel,dtype=int)
|
|
||||||
bitlist[self.bitsperpixel-len(bitlistvaryinglength):]=bitlistvaryinglength
|
|
||||||
return bitlist
|
|
||||||
|
|
||||||
|
|
||||||
def send(self, image,invert=False): #changes slowly 'fadespeed'-pixels at a time
|
|
||||||
global threadrunning
|
global threadrunning
|
||||||
imgmap = []
|
imgmap = []
|
||||||
|
sendbyte=0
|
||||||
|
senddata=bytearray()
|
||||||
|
pixelofbyte=0
|
||||||
for pixel in image.getdata():
|
for pixel in image.getdata():
|
||||||
r, g, b, a = pixel
|
r, g, b, a = pixel
|
||||||
|
|
||||||
pixelbrightness=int( (r+g+b)/3 *(pow(2,self.bitsperpixel)-1) /255 +0.5)
|
pixelbrightness=int( (r+g+b)/3 *(pow(2,self.bitsperpixel)-1) /255 +0.5)
|
||||||
|
sendbyte+=pixelbrightness<<(pixelofbyte*self.bitsperpixel)
|
||||||
|
pixelofbyte+=1
|
||||||
|
if pixelofbyte>=(8/self.bitsperpixel):
|
||||||
|
pixelofbyte=0
|
||||||
|
senddata.append(sendbyte)
|
||||||
|
sendbyte=0
|
||||||
|
|
||||||
if invert:
|
self._sock.sendto(bytes(senddata), (self._udphost, self._udpport))
|
||||||
pixelbrightness=pow(2,self.bitsperpixel)-1-pixelbrightness
|
|
||||||
|
|
||||||
for b in self._intToBitlist(pixelbrightness):
|
|
||||||
imgmap.append(b)
|
|
||||||
|
|
||||||
self.sendPacket(imgmap) #send packet and save last-imagemap
|
|
||||||
|
|
||||||
|
|
||||||
def sendPacket(self, imgmap):
|
|
||||||
packet = self._array2packet(imgmap)
|
|
||||||
self._sock.sendto(bytes(packet), (self._udphost, self._udpport))
|
|
||||||
|
|
||||||
|
|
||||||
def send_bytes(self, img):
|
|
||||||
imgmap = []
|
|
||||||
for pixel in img:
|
|
||||||
if pixel == "1":
|
|
||||||
imgmap.append(1)
|
|
||||||
else:
|
|
||||||
imgmap.append(0)
|
|
||||||
|
|
||||||
if len(img) < 1280:
|
|
||||||
imgmap = np.hstack((imgmap, np.zeros(1280-len(img), dtype=int)))
|
|
||||||
|
|
||||||
packet = self._array2packet(imgmap)
|
|
||||||
|
|
||||||
self._sock.sendto(bytes(packet), (self._udphost, self._udpport))
|
|
||||||
|
|
|
@ -17,6 +17,8 @@ DEBUG=True
|
||||||
BITSPERPIXEL=2
|
BITSPERPIXEL=2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FlipdotSim():
|
class FlipdotSim():
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
imageSize = (160,48), #80,16
|
imageSize = (160,48), #80,16
|
||||||
|
@ -30,82 +32,30 @@ class FlipdotSim():
|
||||||
self.udpHostSocket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
self.udpHostSocket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||||
self.udpHostSocket.bind(("", self.udpPort))
|
self.udpHostSocket.bind(("", self.udpPort))
|
||||||
|
|
||||||
self.timesincelastpacket=time.time()
|
self.time_receivedpacket=0
|
||||||
self.timelastcalculation=0
|
|
||||||
|
self.bitsneeded=self.imageSize[0]*self.imageSize[1]*self.bitsperpixel
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.RunServer()
|
self.RunServer()
|
||||||
|
|
||||||
def bitlistToInt(self,bitlist): #lsb last, [1,0]=2, [0,1]=1
|
|
||||||
number=0
|
|
||||||
for bitindex,bitvalue in enumerate(reversed(bitlist)):
|
|
||||||
number+=bitvalue*pow(2,bitindex)
|
|
||||||
return number
|
|
||||||
|
|
||||||
def RunServer(self):
|
def RunServer(self):
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
#rawData = self.udpHostSocket.recv(4096)
|
|
||||||
rawData = self.udpHostSocket.recv(4096*2)
|
rawData = self.udpHostSocket.recv(4096*2)
|
||||||
|
_fps=1/(time.time()-self.time_receivedpacket)
|
||||||
|
self.time_receivedpacket=time.time()
|
||||||
|
|
||||||
imageArray = ImageArrayAdapter().convertPacketToImageArray(rawData)
|
self.flipdotMatrixSimulatorWidget.showFromRawData(rawData) #send to simulator display
|
||||||
if DEBUG:
|
|
||||||
print("Received Data. Time since last message: "+str( round( time.time()-self.timesincelastpacket,2))+"s ("+str( round( (1/ (time.time()-self.timesincelastpacket) ) ,2) ) +" FPS), last calctime="+str(round( (self.timelastcalculation) ,2)))
|
|
||||||
self.timesincelastpacket=time.time()
|
|
||||||
_bitsneeded=self.imageSize[0]*self.imageSize[1]*self.bitsperpixel
|
|
||||||
if len(imageArray) < _bitsneeded:
|
|
||||||
if DEBUG:
|
|
||||||
print("WARNING: Not enough data received. Got "+str(len(imageArray))+" bits, needs "+str(_bitsneeded)+" bits")
|
|
||||||
emptyArray=np.zeros(_bitsneeded,dtype=int)
|
|
||||||
emptyArray[0:len(imageArray)]=imageArray
|
|
||||||
imageArray=emptyArray
|
|
||||||
elif len(imageArray) > _bitsneeded:
|
|
||||||
if DEBUG:
|
|
||||||
print("WARNING: Too much data received. Got "+str(len(imageArray))+" bits, needs "+str(_bitsneeded)+" bits")
|
|
||||||
imageArray=imageArray[0:_bitsneeded]
|
|
||||||
|
|
||||||
imageArray=[self.bitlistToInt(x) for x in np.array(imageArray).reshape((int(len(imageArray)/self.bitsperpixel),self.bitsperpixel))]
|
calctime=time.time()-self.time_receivedpacket
|
||||||
|
print(str(round(calctime,4))+"s, maxFPS="+str(round(1/calctime,2))+", actual FPS="+str(round(_fps,2))) #calculate time it took for calculation and drawing
|
||||||
self.flipdotMatrixSimulatorWidget.show(imageArray) #send to simulator display
|
|
||||||
|
|
||||||
self.timelastcalculation=time.time()-self.timesincelastpacket #calculate time it took for calculation and drawing
|
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
self.udpHostSocket.close()
|
self.udpHostSocket.close()
|
||||||
|
|
||||||
class ImageArrayAdapter():
|
|
||||||
def __init__(self):
|
|
||||||
self.arrayOfBinaryInts = []
|
|
||||||
|
|
||||||
def convertPacketToImageArray(self, udpPacketStr):
|
|
||||||
self.arrayOfBinaryInts = []
|
|
||||||
byteArray = bytearray(udpPacketStr)
|
|
||||||
|
|
||||||
#Fix for other format. Not Used
|
|
||||||
#byteArray = udpPacketStr.translate(None, b'\r\n').decode().replace('[','').replace(']','').replace(' ','').split(',')
|
|
||||||
#byteArray = [int(x) for x in byteArray]
|
|
||||||
#print("rawtype="+str(type(byteArray)))
|
|
||||||
|
|
||||||
#print(byteArray)
|
|
||||||
for byte in byteArray:
|
|
||||||
#print("byte:"+str(byte))
|
|
||||||
self.__appendByteToArrayOfBinaryInts(byte)
|
|
||||||
return self.arrayOfBinaryInts
|
|
||||||
|
|
||||||
def __appendByteToArrayOfBinaryInts(self, byte):
|
|
||||||
byteValue = int(byte)
|
|
||||||
for i in range(8):
|
|
||||||
if math.floor(byteValue/(2**(7-i))) > 0:
|
|
||||||
self.arrayOfBinaryInts.append(1)
|
|
||||||
#print("append 1")
|
|
||||||
else:
|
|
||||||
self.arrayOfBinaryInts.append(0)
|
|
||||||
#print("append 0")
|
|
||||||
byteValue = byteValue%(2**(7-i))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FlipdotMatrixSimulatorWidget():
|
class FlipdotMatrixSimulatorWidget():
|
||||||
BLACKCOLOR = 0
|
BLACKCOLOR = 0
|
||||||
WHITECOLOR = 1
|
WHITECOLOR = 1
|
||||||
|
@ -119,11 +69,19 @@ class FlipdotMatrixSimulatorWidget():
|
||||||
|
|
||||||
pygame.init()
|
pygame.init()
|
||||||
self.screen = pygame.display.set_mode((imageSize[0]*pixelSize, imageSize[1]*pixelSize))
|
self.screen = pygame.display.set_mode((imageSize[0]*pixelSize, imageSize[1]*pixelSize))
|
||||||
self.screen.fill((255,255,255))
|
|
||||||
|
self.screen.fill((0,0,0))
|
||||||
|
self.pixelsurface=None
|
||||||
_thread.start_new_thread(self.watchCloseThread, ())
|
_thread.start_new_thread(self.watchCloseThread, ())
|
||||||
|
|
||||||
self.bitsperpixel=bitsperpixel
|
self.bitsperpixel=bitsperpixel
|
||||||
|
|
||||||
|
minimumbrightness=32
|
||||||
|
self.colorTable=[]
|
||||||
|
for i in range(pow(2,self.bitsperpixel)):
|
||||||
|
brightness=i/pow(2,self.bitsperpixel)
|
||||||
|
self.colorTable.append( ( int( (255-minimumbrightness)*brightness+minimumbrightness) , int(127*brightness+ minimumbrightness/2), int(minimumbrightness/4) ) )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def watchCloseThread(self):
|
def watchCloseThread(self):
|
||||||
|
@ -134,29 +92,23 @@ class FlipdotMatrixSimulatorWidget():
|
||||||
os.kill(os.getpid(), 9)
|
os.kill(os.getpid(), 9)
|
||||||
pygame.time.delay(500)
|
pygame.time.delay(500)
|
||||||
|
|
||||||
def show(self, imageArray):
|
def showFromRawData(self, rawData):
|
||||||
for yValue in range(self.imageSize[1]):
|
x=0 #pixel x position
|
||||||
for xValue in range(self.imageSize[0]):
|
y=0 #pixel y position
|
||||||
i = self.imageSize[0]*yValue + xValue
|
bitshifts=[x*self.bitsperpixel for x in range(int(8/self.bitsperpixel))]
|
||||||
color = imageArray[i] / (pow(2,self.bitsperpixel)-1) #gives a number between 0 and 1 inclusive. Example for bitsperpixel=2: [1,0]-> 2/ (2^2-1)=2/3
|
bitmask=int('00000011', 2)
|
||||||
self.updatePixel(xValue, yValue, color)
|
|
||||||
|
for cbyte in rawData:
|
||||||
|
for numbershifts in bitshifts:
|
||||||
|
pixelbyte = ( cbyte & (bitmask<<numbershifts) ) >> numbershifts
|
||||||
|
|
||||||
|
pygame.draw.circle(self.screen, self.colorTable[pixelbyte], (int(x*self.pixelSize+self.pixelSize/2), int(y*self.pixelSize+self.pixelSize/2)), int(self.pixelSize/2)) #Draw LED as filled circles
|
||||||
|
|
||||||
|
x+=1 #next pixel
|
||||||
|
y+=int(x/self.imageSize[0]) #next column if end of row
|
||||||
|
x%=self.imageSize[0] #start at first column if end of row
|
||||||
|
|
||||||
pygame.display.update()
|
pygame.display.update()
|
||||||
|
|
||||||
def clearPixels(self):
|
|
||||||
for xValue in range(self.imageSize[0]):
|
|
||||||
for yValue in range(self.imageSize[1]):
|
|
||||||
self.updatePixel(xValue, yValue, self.BLACKCOLOR)
|
|
||||||
|
|
||||||
def updatePixel(self, xValue, yValue, brightness):
|
|
||||||
surface = pygame.Surface((self.pixelSize, self.pixelSize))
|
|
||||||
minimumbrightness=32
|
|
||||||
rectcolor = ( (255-minimumbrightness)*brightness+minimumbrightness,127*brightness+ minimumbrightness/2, minimumbrightness/4)
|
|
||||||
|
|
||||||
#surface.fill(rectcolor)
|
|
||||||
pygame.draw.circle(surface, rectcolor, (int(self.pixelSize/2), int(self.pixelSize/2)), int(self.pixelSize/2)) #Draw LED as filled circles
|
|
||||||
self.screen.blit(surface, (xValue*self.pixelSize, yValue*self.pixelSize))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
FlipdotSim(imageSize=(WIDTH,HEIGHT), pixelSize = PIXELSIZE, udpPort=UDPPORT, bitsperpixel=BITSPERPIXEL).run()
|
FlipdotSim(imageSize=(WIDTH,HEIGHT), pixelSize = PIXELSIZE, udpPort=UDPPORT, bitsperpixel=BITSPERPIXEL).run()
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
import hypermedia.net.*;
|
||||||
|
|
||||||
|
int pixelSize = 4;
|
||||||
|
int bitPerPixel = 2;
|
||||||
|
int width=160;
|
||||||
|
int height=144;
|
||||||
|
int windowWidth=width*pixelSize;
|
||||||
|
int windowHeight=height*pixelSize;
|
||||||
|
|
||||||
|
UDP udp;
|
||||||
|
|
||||||
|
byte[] emptyByteArray = new byte[0];
|
||||||
|
byte receivedData[]=emptyByteArray;
|
||||||
|
byte imagebuffer[][]=new byte[width][height];
|
||||||
|
|
||||||
|
PImage led[]=new PImage[(int)pow(2,bitPerPixel)];
|
||||||
|
|
||||||
|
void settings() {
|
||||||
|
size(width*pixelSize, height*pixelSize,P2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
//Setup Graphics
|
||||||
|
|
||||||
|
frameRate(30);
|
||||||
|
noSmooth();
|
||||||
|
background(0);
|
||||||
|
noStroke();
|
||||||
|
|
||||||
|
//UDP
|
||||||
|
udp = new UDP( this, 2323 );
|
||||||
|
//udp.log( true ); // <-- printout the connection activit
|
||||||
|
udp.listen( true );
|
||||||
|
|
||||||
|
//Precompute LED Images
|
||||||
|
for (byte b=0;b<led.length;b++){
|
||||||
|
led[b] = createImage(pixelSize, pixelSize, RGB);
|
||||||
|
led[b].loadPixels();
|
||||||
|
for (int px = 0; px < pixelSize; px++) {
|
||||||
|
for (int py = 0; py < pixelSize; py++) {
|
||||||
|
float distanceFromCenter=sqrt( pow(px-pixelSize/2,2) + pow(py-pixelSize/2,2) ) / sqrt(pow(pixelSize/2,2) + pow(pixelSize/2,2));
|
||||||
|
int colorR=255/3*b;
|
||||||
|
int colorG=127/3*b;
|
||||||
|
int colorB=0;
|
||||||
|
float brightnessMultiplier=map(distanceFromCenter, 0.4, 1/sqrt(2), 1.0, 0.0);
|
||||||
|
if(brightnessMultiplier<0){
|
||||||
|
brightnessMultiplier=0;
|
||||||
|
}else if(brightnessMultiplier>1){
|
||||||
|
brightnessMultiplier=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
colorR*=brightnessMultiplier;
|
||||||
|
colorG*=brightnessMultiplier;
|
||||||
|
colorB*=brightnessMultiplier;
|
||||||
|
|
||||||
|
led[b].pixels[py*pixelSize+px] = color(colorR,colorG,colorB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
led[b].updatePixels();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//draw event handler
|
||||||
|
void draw() {
|
||||||
|
//map byte data to imagebuffer
|
||||||
|
if (receivedData.length > 0){ //new data?
|
||||||
|
byte[] cachedData=receivedData; //cache data to free array for new data
|
||||||
|
receivedData = emptyByteArray;
|
||||||
|
|
||||||
|
for (int i=0;i<cachedData.length;i++){
|
||||||
|
for (int shift=0;shift<(8/bitPerPixel);shift++){ //bitshifts because a byte can contain multiple pixels
|
||||||
|
int pixelnumber=i*(8/bitPerPixel)+shift; //absolute pixelnumber
|
||||||
|
int x=pixelnumber%width;
|
||||||
|
int y=pixelnumber/width;
|
||||||
|
int value= (cachedData[i]&( 3 <<(shift*bitPerPixel))) >> (shift*bitPerPixel); //mask relevant bits for current pixel
|
||||||
|
|
||||||
|
imagebuffer[x][y]=(byte)value; //write pixel value to image buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(); //clear screen
|
||||||
|
|
||||||
|
//int timeA=millis();
|
||||||
|
|
||||||
|
//Draw leds
|
||||||
|
for (int x = 0; x < imagebuffer.length; x++){
|
||||||
|
for (int y = 0; y < imagebuffer[x].length; y++){
|
||||||
|
byte pixel=imagebuffer[x][y];
|
||||||
|
|
||||||
|
//fill(255/3*pixel,127/3*pixel,0); //set pixel color
|
||||||
|
//ellipse(x*pixelSize+pixelSize/2, y*pixelSize+pixelSize/2, pixelSize, pixelSize); //Very slow
|
||||||
|
//rect(x*pixelSize, y*pixelSize, pixelSize, pixelSize); //works fast enough
|
||||||
|
|
||||||
|
image(led[pixel], x*pixelSize, y*pixelSize); //draw precomputed led image, as fast as rect but looks nicer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//int timeB=millis()-timeA;
|
||||||
|
//println("Time="+(timeB));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//udp receive event handler
|
||||||
|
void receive( byte[] data, String ip, int port ) {
|
||||||
|
receivedData=data; //received data to buffer
|
||||||
|
}
|
|
@ -9,14 +9,17 @@ matrix = MatrixSender(udphost="localhost", udpport=2323, img_size=(160,24*6), bi
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
_fps=0
|
||||||
|
_time_startsending=0
|
||||||
|
|
||||||
im = Image.new("RGBA", matrix._img_size, MatrixSender.C_BLACK) #create image
|
im = Image.new("RGBA", matrix._img_size, MatrixSender.C_BLACK) #create image
|
||||||
draw = ImageDraw.Draw(im) #get draw instance
|
draw = ImageDraw.Draw(im) #get draw instance
|
||||||
|
|
||||||
|
|
||||||
ball_pos=np.array((20,20))
|
ball_pos=np.array((20,20))
|
||||||
ball_size=10
|
ball_size=10
|
||||||
ball_vel=np.array((-5,5))
|
ball_vel=np.array((-5,5))
|
||||||
|
|
||||||
|
|
||||||
while(True):
|
while(True):
|
||||||
#------- Update Movements ------
|
#------- Update Movements ------
|
||||||
#move ball
|
#move ball
|
||||||
|
@ -34,7 +37,11 @@ if __name__ == '__main__':
|
||||||
#draw ball
|
#draw ball
|
||||||
draw.ellipse((ball_pos[0]-ball_size/2,ball_pos[1]-ball_size/2,ball_pos[0]+ball_size/2,ball_pos[1]+ball_size/2,),fill=(255,255,255))
|
draw.ellipse((ball_pos[0]-ball_size/2,ball_pos[1]-ball_size/2,ball_pos[0]+ball_size/2,ball_pos[1]+ball_size/2,),fill=(255,255,255))
|
||||||
|
|
||||||
|
_fps=1/(time.time()-_time_startsending)
|
||||||
|
_time_startsending=time.time()
|
||||||
matrix.send(im) #construct udp packet and send to matrix
|
matrix.send(im) #construct udp packet and send to matrix
|
||||||
|
_time_endsending=time.time()
|
||||||
|
|
||||||
time.sleep(.1) #wait
|
print("Sendtime="+str(round(_time_endsending-_time_startsending,4))+"s, maxFPS="+str(round(1/(_time_endsending-_time_startsending),2))+", actual FPS="+str(round(_fps,2)))
|
||||||
|
|
||||||
|
#time.sleep(.1) #wait
|
||||||
|
|
Loading…
Reference in a new issue