add simulator and client image sender with 2 bit per pixel capability
BIN
client_sw/__pycache__/matrixSender.cpython-35.pyc
Normal file
37
client_sw/images.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env python3
|
||||
import time
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from matrixSender import MatrixSender
|
||||
import time
|
||||
import sys
|
||||
from random import randint
|
||||
|
||||
matrix = MatrixSender(udphost="localhost", udpport=2323, img_size=(160,24*6), bitsperpixel=2)
|
||||
|
||||
|
||||
def send_img_from_filename(imgfilename,invert=False):
|
||||
background = Image.new("RGBA", matrix._img_size, MatrixSender.C_BLACK)
|
||||
image = Image.open(imgfilename)
|
||||
image = image.convert(mode='RGBA')
|
||||
image = image.resize(matrix._img_size)
|
||||
image.save('/tmp/send2.png', 'PNG')
|
||||
background.paste(image, box=(0,0), mask=None)
|
||||
background.save('/tmp/send.png', 'PNG')
|
||||
|
||||
matrix.send(background,invert)
|
||||
|
||||
def send_img(img):
|
||||
background = Image.new("RGBA", matrix._img_size, MatrixSender.C_BLACK)
|
||||
stream = io.BytesIO(img)
|
||||
image = Image.open(stream)
|
||||
image = image.convert(mode='RGBA')
|
||||
image = image.resize(matrix._img_size)
|
||||
image.save('/tmp/send2.jpeg', 'JPEG')
|
||||
background.paste(image, box=(0,0), mask=None)
|
||||
background.save('/tmp/send.jpeg', 'JPEG')
|
||||
|
||||
matrix.send(background)
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv)>1:
|
||||
send_img_from_filename(sys.argv[1])
|
BIN
client_sw/images/160_144/grauverlauf.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
client_sw/images/160_144/pokemon1.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
client_sw/images/160_48/Spaceinvaders.png
Normal file
After Width: | Height: | Size: 368 B |
BIN
client_sw/images/160_48/black.png
Normal file
After Width: | Height: | Size: 179 B |
BIN
client_sw/images/160_48/chaoswest.png
Normal file
After Width: | Height: | Size: 712 B |
BIN
client_sw/images/160_48/ctdo_logo.png
Normal file
After Width: | Height: | Size: 474 B |
BIN
client_sw/images/160_48/kirby.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
client_sw/images/160_48/kirbyboss.png
Normal file
After Width: | Height: | Size: 1,006 B |
BIN
client_sw/images/160_48/mario.png
Normal file
After Width: | Height: | Size: 530 B |
BIN
client_sw/images/160_48/nyancat.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
client_sw/images/160_48/pokemon_wildpidgey.png
Normal file
After Width: | Height: | Size: 904 B |
BIN
client_sw/images/160_48/schlange.png
Normal file
After Width: | Height: | Size: 656 B |
BIN
client_sw/images/160_48/tetris.png
Normal file
After Width: | Height: | Size: 986 B |
BIN
client_sw/images/160_48/troll.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
client_sw/images/160_48/tuwat.png
Normal file
After Width: | Height: | Size: 494 B |
BIN
client_sw/images/160_48/white.png
Normal file
After Width: | Height: | Size: 232 B |
BIN
client_sw/images/160_48/zelda.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
93
client_sw/matrixSender.py
Normal file
|
@ -0,0 +1,93 @@
|
|||
from PIL import Image, ImageDraw, ImageFont
|
||||
import socket
|
||||
import binascii
|
||||
import io
|
||||
import time
|
||||
import re
|
||||
import numpy as np
|
||||
|
||||
import PIL.ImageOps
|
||||
|
||||
|
||||
|
||||
class MatrixSender(object):
|
||||
|
||||
C_BLACK = 0
|
||||
C_WHITE = 255
|
||||
|
||||
global threadrunning
|
||||
threadrunning=False
|
||||
|
||||
|
||||
def __init__(self, udphost, udpport, img_size=(160,48), bitsperpixel=1):
|
||||
|
||||
self._udphost = udphost
|
||||
if not type(udpport) is int or udpport > 65536:
|
||||
raise TypeError('port has to be int and > 65536 !!')
|
||||
self._udpport = udpport
|
||||
self._img_size = img_size
|
||||
|
||||
self.bitsperpixel=bitsperpixel
|
||||
|
||||
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
|
||||
|
||||
|
||||
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
|
||||
#if fadespeed=0 -> change instant.
|
||||
#time to change= 1280/25*0.2
|
||||
imgmap = []
|
||||
for pixel in image.getdata():
|
||||
r, g, b, a = pixel
|
||||
|
||||
pixelbrightness=int( (r+g+b)/3 *(pow(2,self.bitsperpixel)-1) /255 +0.5)
|
||||
|
||||
|
||||
if invert:
|
||||
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))
|
159
client_sw/matrixsimulator.py
Normal file
|
@ -0,0 +1,159 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: UTF-8 -*-
|
||||
import _thread
|
||||
import socket
|
||||
import pygame
|
||||
import math
|
||||
import random
|
||||
import numpy as np
|
||||
from pygame.locals import *
|
||||
import time
|
||||
|
||||
WIDTH=160
|
||||
HEIGHT=24*6
|
||||
PIXELSIZE=4 #For Simulator Display
|
||||
UDPPORT=2323
|
||||
DEBUG=True
|
||||
BITSPERPIXEL=2
|
||||
|
||||
|
||||
class FlipdotSim():
|
||||
def __init__(self,
|
||||
imageSize = (160,48), #80,16
|
||||
pixelSize = 10,
|
||||
udpPort = 2323,
|
||||
bitsperpixel=1):
|
||||
self.imageSize=imageSize
|
||||
self.udpPort = udpPort
|
||||
self.bitsperpixel=bitsperpixel
|
||||
self.flipdotMatrixSimulatorWidget = FlipdotMatrixSimulatorWidget(imageSize, pixelSize, bitsperpixel) #comment out if no simulator needed
|
||||
self.udpHostSocket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
self.udpHostSocket.bind(("", self.udpPort))
|
||||
|
||||
self.timesincelastpacket=time.time()
|
||||
|
||||
def run(self):
|
||||
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):
|
||||
try:
|
||||
while True:
|
||||
#rawData = self.udpHostSocket.recv(4096)
|
||||
rawData = self.udpHostSocket.recv(4096*2)
|
||||
|
||||
imageArray = ImageArrayAdapter().convertPacketToImageArray(rawData)
|
||||
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)")
|
||||
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))]
|
||||
|
||||
self.flipdotMatrixSimulatorWidget.show(imageArray) #send to simulator display
|
||||
|
||||
finally:
|
||||
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():
|
||||
BLACKCOLOR = 0
|
||||
WHITECOLOR = 1
|
||||
|
||||
def __init__(self,
|
||||
imageSize = (160,48),
|
||||
pixelSize = 4,
|
||||
bitsperpixel = 1):
|
||||
self.imageSize = imageSize
|
||||
self.pixelSize = pixelSize
|
||||
|
||||
pygame.init()
|
||||
self.screen = pygame.display.set_mode((imageSize[0]*pixelSize, imageSize[1]*pixelSize))
|
||||
self.screen.fill((255,255,255))
|
||||
_thread.start_new_thread(self.watchCloseThread, ())
|
||||
|
||||
self.bitsperpixel=bitsperpixel
|
||||
|
||||
|
||||
|
||||
def watchCloseThread(self):
|
||||
while True:
|
||||
for event in pygame.event.get():
|
||||
if event.type in (QUIT, QUIT):
|
||||
import os
|
||||
os.kill(os.getpid(), 9)
|
||||
pygame.time.delay(500)
|
||||
|
||||
def show(self, imageArray):
|
||||
for yValue in range(self.imageSize[1]):
|
||||
for xValue in range(self.imageSize[0]):
|
||||
i = self.imageSize[0]*yValue + xValue
|
||||
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
|
||||
self.updatePixel(xValue, yValue, color)
|
||||
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__':
|
||||
FlipdotSim(imageSize=(WIDTH,HEIGHT), pixelSize = PIXELSIZE, udpPort=UDPPORT, bitsperpixel=BITSPERPIXEL).run()
|