====== BlinkStröm ====== ==== Dokumentation der im Space vorhandenen BlinkStrömAdvanced-Geräte ==== Es existiert ein Gerät in jeweils blau und rot. Direkt nach dem ersten Auftauchen am 20. November 2k12 wurden erfolgreich Konverter implementiert um über die FAT16-formatierte SD-Karte eigene Daten anzuzeigen. === Externe Dokumentation === * [[http://wiki.blinkenarea.org/index.php/BlinkstroemAdvanced|blinkenarena]] hat dazu Details * vermutlich aus dem [[http://wiki.blinkenarea.org/index.php/BlinkenLEDsPlus|BlinkenLEDsPlus]] hervorgegangen === Code-Schnippsel === (Oder besser irgendwo in ein public code repo?) Common Lisp code for viewing and generating new animations: [[https://github.com/plops/cl-blinkstroem]] == quick&dirty Python Converter == # -*- coding: utf-8 -*- # use it as you like, feel free to improve! import sys import os.path from math import log10 # using numpy for convienent bytewise array manipulation import numpy as np # using Qt for reading arbitrary full color image files from PyQt4.QtGui import QImage, QColor WIDTH, HEIGHT = 18, 8 # display dimension def readAnim(inFile, outDir): assert os.path.isfile(inFile), \ "Given file '{0}' does not exist!".format(inFile) assert os.path.isdir(outDir), \ "Given target directory '{0}' does not exist!".format(outDir) data = None with open(inFile, 'rb') as fd: data = bytearray(fd.read()) prefixSize = 2 frameSize = WIDTH * HEIGHT + prefixSize frameCount = len(data) / frameSize fnfmt = "{0:0"+str(int(log10(frameCount) + 1.))+"5d}.png" for img in range(0, frameCount): framedata = np.array(data[frameSize*img:frameSize*(img+1)]) # strip duration bytes and convert to 4 byte value framedata = framedata[2:].astype(np.uint32) frame = QImage(framedata, WIDTH, HEIGHT, QImage.Format_RGB32) # create proper image and save outFile = os.path.join(outDir, fnfmt.format(img)) frame.save(outFile) print "Wrote {0} files to '{1}'.".format(frameCount, outDir) def writeAnim(imageFile, frameCount, outFile): assert os.path.isfile(imageFile) print "using image '{0}' ...".format(imageFile) if os.path.isfile(outFile): print "Overwriting '{0}'!".format(outFile) duration = 500 # ms intensitySteps = 7 frame = QImage(imageFile) dimension = WIDTH, HEIGHT assert frame.width() in dimension and frame.height() in dimension, \ "Input image as to be of dimension '{}'!".format(dimension) bytesPerPixel = frame.depth() / 8 # read a color image as bytearray into a numpy array framedata = np.array(bytearray(frame.bits().asstring(frame.byteCount())), dtype = np.dtype("u{0}".format(bytesPerPixel))) # reshape as matrix with multiple bytes per pixel framedata = framedata.reshape(frame.height(), frame.width(), bytesPerPixel) # sum up all colors framedata = np.add.reduce(framedata, axis = 2) # normalize: substract constant alpha value, scale intensity, fixed pixel size framedata -= framedata.min() framedata /= int(float(framedata.max()) / intensitySteps) framedata = framedata.astype(np.uint8).T blankframe = np.zeros_like(framedata) fd = open(outFile, "wb") duration = np.array((int(duration/20),), dtype=np.uint16).byteswap(True) # for comparison with ref.bin # duration = np.array((50,), dtype=np.uint16).byteswap(True) for img in range(0, frameCount): fd.write(duration) # every second frame is a blank one == blinking if img%2 == 0: fd.write(framedata.ravel()) else: fd.write(blankframe.ravel()) print "done." def usage(): print """ usage: {0} Creates a .bin file containing provided image blinking. {0} Parses the given .bin file and extracts all frame as single PNG images to the given directory. """ args = sys.argv if len(args) > 1 and os.path.isdir(args[-1]) and os.path.isfile(args[-2]): readAnim(args[-2], args[-1]) elif len(args) > 1 and os.path.isfile(args[-2]): frameCount = 1000 writeAnim(args[-2], frameCount, args[-1]) else: usage()