168 lines
5.8 KiB
Python
168 lines
5.8 KiB
Python
#!/usr/bin/env python
|
|
|
|
class Image(object):
|
|
def __init__(self, content):
|
|
self.content = content
|
|
|
|
def load(self):
|
|
return self
|
|
|
|
def __getitem__(self, item):
|
|
if isinstance(item, tuple):
|
|
x, y = item
|
|
return Image('{0}[(ROW({1}) + COL({2})) * 3'.format(self.content, y, x))
|
|
elif isinstance(item, int):
|
|
return Image('{0} + RGB({1})]'.format(self.content, item))
|
|
else:
|
|
raise NotImplementedError()
|
|
|
|
def __rmul__(self, n):
|
|
return Image('({1} * {0})'.format(self.content, float(n)))
|
|
|
|
def __mul__(self, n):
|
|
return Image('({0} * {1})'.format(self.content, float(n)))
|
|
|
|
def __rtruediv__(self, n):
|
|
return Image('({1} / {0})'.format(self.content, n))
|
|
|
|
def __truediv__(self, n):
|
|
return Image('({0} / {1})'.format(self.content, n))
|
|
|
|
def __radd__(self, n):
|
|
return Image('({1} + {0})'.format(self.content, n))
|
|
|
|
def __add__(self, n):
|
|
return Image('({0} + {1})'.format(self.content, n))
|
|
|
|
def __str__(self):
|
|
return self.content
|
|
|
|
from pysstv.color import MartinM1, MartinM2, PasokonP3, PasokonP5, PasokonP7
|
|
import re
|
|
|
|
supported = [MartinM1, MartinM2, PasokonP3, PasokonP5, PasokonP7]
|
|
ROW_RE = re.compile(r'ROW\(\d+\)')
|
|
|
|
def main(sstv_class=None):
|
|
if sstv_class is None:
|
|
sstv_class = MartinM1
|
|
elif sstv_class not in supported:
|
|
raise NotImplementedError()
|
|
sstv = sstv_class(Image('img'), 44100, 16)
|
|
n = 0
|
|
yield '#define ROW(x) x'
|
|
yield '#define COL(x) x'
|
|
yield '#define RGB(x) x'
|
|
yield 'void convert(unsigned char *img, float *freqs, float *msecs, const int width) {\nint frq = 0;'
|
|
history = []
|
|
lut = {}
|
|
same_as = {}
|
|
for freq, msec in sstv.gen_freq_bits():
|
|
printed = 'freqs[frq] = {1}; msecs[frq++] = {2};'.format(n, freq, msec)
|
|
key = ROW_RE.sub('row', printed)
|
|
old = lut.get(key)
|
|
if old is not None:
|
|
same_as[n] = old
|
|
else:
|
|
lut[key] = n
|
|
history.append((printed, key))
|
|
n += 1
|
|
del lut
|
|
m_start, m_len = gen_matches(same_as, history, n)
|
|
for i in xrange(same_as[m_start]):
|
|
yield history[i][0]
|
|
yield 'for (int row = 0; row < width * {0}; row += width) {{'.format(sstv.HEIGHT)
|
|
for i in xrange(same_as[m_start], same_as[m_start] + m_len - 1):
|
|
yield ' ' + history[i][1]
|
|
yield '}'
|
|
yield '}}\n\n#define FREQ_COUNT {0}'.format(n)
|
|
|
|
|
|
|
|
def gen_matches(same_as, history, n):
|
|
cur_start = None
|
|
cur_len = None
|
|
cur_end = None
|
|
for i in xrange(n):
|
|
if cur_start is None:
|
|
tmp = same_as.get(i)
|
|
if tmp is not None:
|
|
cur_len = 1
|
|
cur_start = i
|
|
cur_end = tmp
|
|
else:
|
|
tmp = same_as.get(i)
|
|
if tmp is not None and history[tmp][1] == history[cur_end + 1][1] and cur_start > cur_end:
|
|
cur_len += 1
|
|
cur_end += 1
|
|
else:
|
|
if tmp is not None and history[tmp][1] == history[cur_end + 1][1]:
|
|
return cur_start, cur_len
|
|
tmp = same_as.get(i)
|
|
if tmp is None:
|
|
cur_start = None
|
|
else:
|
|
cur_len = 1
|
|
cur_start = i
|
|
cur_end = tmp
|
|
|
|
def test(img_file):
|
|
from subprocess import Popen, PIPE, check_output
|
|
from os import remove, path
|
|
from PIL import Image
|
|
from datetime import datetime
|
|
import struct
|
|
exe = './codegen-test-executable'
|
|
if not path.exists('stb_image.h'):
|
|
from urllib import urlretrieve
|
|
urlretrieve('https://raw.githubusercontent.com/nothings/stb/master/stb_image.h', 'stb_image.h')
|
|
try:
|
|
for sstv_class in supported:
|
|
print 'Testing', sstv_class
|
|
gcc = Popen(['gcc', '-xc', '-lm', '-o', exe, '-'], stdin=PIPE)
|
|
start = datetime.now()
|
|
with open(path.join(path.dirname(__file__), 'codeman.c')) as cm:
|
|
c_src = cm.read().replace('#include "codegen.c"', '\n'.join(main(sstv_class)))
|
|
gcc.communicate(c_src)
|
|
gen_elapsed = datetime.now() - start
|
|
print ' - gengcc took', gen_elapsed
|
|
start = datetime.now()
|
|
gen = check_output([exe, img_file])
|
|
native_elapsed = datetime.now() - start
|
|
print ' - native took', native_elapsed
|
|
img = Image.open(img_file)
|
|
sstv = sstv_class(img, 44100, 16)
|
|
start = datetime.now()
|
|
try:
|
|
for n, (freq, msec) in enumerate(sstv.gen_freq_bits()):
|
|
assert gen[n * 8:(n + 1) * 8] == struct.pack('ff', freq, msec)
|
|
except AssertionError:
|
|
mode_name = sstv_class.__name__
|
|
with file('/tmp/{0}-c.bin'.format(mode_name), 'wb') as f:
|
|
f.write(gen)
|
|
with file('/tmp/{0}-py.bin'.format(mode_name), 'wb') as f:
|
|
for n, (freq, msec) in enumerate(sstv.gen_freq_bits()):
|
|
f.write(struct.pack('ff', freq, msec))
|
|
with file('/tmp/{0}.c'.format(mode_name), 'w') as f:
|
|
f.write(c_src)
|
|
print (" ! Outputs are different, they've been saved to "
|
|
"/tmp/{0}-{{c,py}}.bin, along with the C source code "
|
|
"in /tmp/{0}.c").format(mode_name)
|
|
python_elapsed = datetime.now() - start
|
|
print ' - python took', python_elapsed
|
|
print ' - speedup:', python_elapsed.total_seconds() / native_elapsed.total_seconds()
|
|
print 'OK'
|
|
finally:
|
|
try:
|
|
remove(exe)
|
|
except OSError:
|
|
pass
|
|
|
|
|
|
if __name__ == '__main__':
|
|
from sys import argv
|
|
if len(argv) > 2 and argv[1] == 'test':
|
|
test(argv[2])
|
|
else:
|
|
print '\n'.join(main())
|