Extract out input video decoding into separate module.

Prototype a threaded version of the decoder but this doesn't seem to be
necessary as it's not the bottleneck.

Opcode stream is now aware of frame cycle budget and will keep emitting
until budget runs out -- so no need for fullness estimate.
This commit is contained in:
kris 2019-02-23 23:32:07 +00:00
parent dc671986a3
commit e4174ed10b
2 changed files with 58 additions and 144 deletions

34
frame_sequence.py Normal file
View File

@ -0,0 +1,34 @@
from typing import Iterable
import threading
import queue
import numpy
import skvideo.io
def frame_sequence(filename: str) -> Iterable[numpy.ndarray]:
yield from skvideo.io.vreader(filename)
# Version of the above that delegates decoding to a background thread; not
# clear that it's more efficient though
def frame_sequence2(filename: str) -> Iterable[numpy.ndarray]:
q = queue.Queue()
def worker():
for f in skvideo.io.vreader(filename):
q.put(f)
q.put(None)
t = threading.Thread(target=worker)
t.start()
while True:
frame = q.get()
if frame is None:
break
yield frame
q.task_done()
t.join()

168
main.py
View File

@ -1,135 +1,31 @@
import skvideo.io
import skvideo.datasets
from PIL import Image
import numpy as np
import frame_sequence
import opcodes
import screen
import video
CYCLES = 1024 * 1024
MAX_OUT = 20 * 1024
MAX_OUT = 1 * 1024 * 1024
VIDEO_FPS = 30
APPLE_FPS = 10
# Old naive XOR algorithm:
#
# stores=1894, content changes=15, page changes=365
# Frame 0, 2654 bytes, similarity = 0.850856
# stores=1750, content changes=19, page changes=444
# Frame 3, 2676 bytes, similarity = 0.903088
# stores=1648, content changes=20, page changes=501
# Frame 6, 2690 bytes, similarity = 0.922024
# stores=1677, content changes=18, page changes=486
# Frame 9, 2685 bytes, similarity = 0.912723
# stores=1659, content changes=18, page changes=497
# Frame 12, 2689 bytes, similarity = 0.923438
# stores=1681, content changes=17, page changes=485
# Frame 15, 2685 bytes, similarity = 0.922656
# stores=1686, content changes=17, page changes=482
# Frame 18, 2684 bytes, similarity = 0.921912
# stores=1669, content changes=17, page changes=492
# New
# stores=2260, content changes=277, page changes=125
# Frame 0, 3064 bytes, similarity = 0.874740
# stores=2162, content changes=325, page changes=131
# Frame 3, 3074 bytes, similarity = 0.925670
# stores=2241, content changes=313, page changes=102
# Frame 6, 3071 bytes, similarity = 0.936942
# stores=2265, content changes=313, page changes=90
# Frame 9, 3071 bytes, similarity = 0.931882
# stores=2225, content changes=334, page changes=91
# Frame 12, 3075 bytes, similarity = 0.929427
# stores=2216, content changes=342, page changes=89
# Frame 15, 3078 bytes, similarity = 0.919978
# stores=2222, content changes=339, page changes=88
# Optimized new
# Fullness = 1.384560, cycle_counter = 90738/104857 budget
# stores=1872, content changes=15, page changes=352
# Frame 0, 2606 bytes, similarity = 0.849219
# Fullness = 1.452588, cycle_counter = 110009/104857 budget
# stores=2163, content changes=28, page changes=472
# Frame 3, 3163 bytes, similarity = 0.924256
# Fullness = 1.577072, cycle_counter = 113843/104857 budget
# stores=2062, content changes=30, page changes=577
# Frame 6, 3276 bytes, similarity = 0.939918
# Fullness = 1.597466, cycle_counter = 106213/104857 budget
# stores=1899, content changes=29, page changes=550
# Frame 9, 3057 bytes, similarity = 0.928274
# Fullness = 1.615001, cycle_counter = 106008/104857 budget
# stores=1875, content changes=27, page changes=561
# Frame 12, 3051 bytes, similarity = 0.933854
# Fullness = 1.639691, cycle_counter = 106460/104857 budget
# stores=1855, content changes=30, page changes=575
# Frame 15, 3065 bytes, similarity = 0.929725
# Fullness = 1.635406, cycle_counter = 104583/104857 budget
# stores=1827, content changes=30, page changes=562
# TSP solver
# Fullness = 1.336189, cycle_counter = 87568/104857 budget
# stores=1872, content changes=320, page changes=32
# Frame 0, 2576 bytes, similarity = 0.849219
# Fullness = 1.386065, cycle_counter = 108771/104857 budget
# stores=2242, content changes=452, page changes=33
# Frame 3, 3212 bytes, similarity = 0.927604
# Fullness = 1.482284, cycle_counter = 112136/104857 budget
# stores=2161, content changes=552, page changes=33
# Frame 6, 3331 bytes, similarity = 0.943415
# Fullness = 1.501014, cycle_counter = 106182/104857 budget
# stores=2021, content changes=535, page changes=33
# Frame 9, 3157 bytes, similarity = 0.934263
# Fullness = 1.523818, cycle_counter = 106450/104857 budget
# stores=1995, content changes=554, page changes=33
# Frame 12, 3169 bytes, similarity = 0.939844
# Fullness = 1.543029, cycle_counter = 106179/104857 budget
# stores=1966, content changes=566, page changes=33
# Frame 15, 3164 bytes, similarity = 0.935231
# Fullness = 1.538659, cycle_counter = 104560/104857 budget
# stores=1941, content changes=554, page changes=33
# page first
# Fullness = 1.366463, cycle_counter = 89552/104857 budget
# stores=1872, content changes=352, page changes=32
# Frame 0, 2640 bytes, similarity = 0.849219
# Fullness = 1.413155, cycle_counter = 108440/104857 budget
# stores=2192, content changes=476, page changes=32
# Frame 3, 3208 bytes, similarity = 0.925744
# Fullness = 1.516888, cycle_counter = 112554/104857 budget
# stores=2120, content changes=583, page changes=32
# Frame 6, 3350 bytes, similarity = 0.942187
# Fullness = 1.535086, cycle_counter = 106115/104857 budget
# stores=1975, content changes=561, page changes=32
# Frame 9, 3161 bytes, similarity = 0.932106
# Fullness = 1.553913, cycle_counter = 106143/104857 budget
# stores=1951, content changes=575, page changes=32
# Frame 12, 3165 bytes, similarity = 0.937835
# Fullness = 1.571548, cycle_counter = 106047/104857 budget
# stores=1927, content changes=587, page changes=32
# Frame 15, 3165 bytes, similarity = 0.933259
# Fullness = 1.572792, cycle_counter = 104940/104857 budget
# stores=1906, content changes=581, page changes=32
def main():
s = video.Video()
frames = frame_sequence.frame_sequence(
"Computer Chronicles - 06x05 - The Apple II.mp4")
bytes_out = 0
s = video.Video(APPLE_FPS)
screen_cls = screen.HGR140Bitmap
# Assert that the opcode stream reconstructs the same screen
ds = video.Video()
decoder = opcodes.Decoder(s.state)
videogen = skvideo.io.vreader("CoffeeCup-H264-75.mov")
with open("out.bin", "wb") as out:
bytes_out = 0
# Estimated opcode overhead, i.e. ratio of extra cycle_counter from
# opcodes
fullness = 1.6
screen_cls = screen.HGRBitmap
# Assert that the opcode stream reconstructs the same screen
ds = video.Video()
decoder = opcodes.Decoder(ds.state)
for idx, frame in enumerate(videogen):
for idx, frame in enumerate(frames):
if idx % (VIDEO_FPS // APPLE_FPS):
continue
@ -141,50 +37,34 @@ def main():
f = screen_cls(im)
cycle_budget = int(CYCLES / APPLE_FPS)
stream = bytes(s.update(f, cycle_budget, fullness))
#print(" ".join("%02x(%02d)" % (b, b) for b in stream))
fullness *= s.cycle_counter.cycles / cycle_budget
print("Fullness = %f, cycle_counter = %d/%d budget" % (
fullness, s.cycle_counter.cycles, cycle_budget))
(num_content_stores, num_content_changes,
num_page_changes, num_rle_bytes) = decoder.decode_stream(
iter(stream))
assert np.array_equal(ds.screen.bytemap, s.screen.bytemap), (
ds.screen.bytemap ^ s.screen.bytemap)
print("stores=%d, content changes=%d, page changes=%d, "
"rle_bytes=%d" % (
num_content_stores, num_content_changes,
num_page_changes, num_rle_bytes))
stream = bytes(s.emit_stream(s.encode_frame(f)))
# assert that the screen decodes to the original bitmap
bm = screen_cls.from_bytemap(s.screen).bitmap
# print(np.array(im)[0:5,0:5])
# print(bm[0:5,0:5])
# print("Comparing bitmaps")
# print(np.array(im))
# print(bm)
# print(s.screen)
# assert np.array_equal(bm, im), np.ma.masked_array(
#np.set_printoptions(threshold=100000000)
#assert np.array_equal(bm, im), np.ma.masked_array(
# bm, np.logical_not(np.logical_xor(bm, im)))
# d = Image.fromarray(s.screen)
# d.show()
bytes_out += len(stream)
if bytes_out > MAX_OUT:
break
bytes_left = MAX_OUT - bytes_out
print("Frame %d, %d bytes, similarity = %f" % (
idx, len(stream), screen.bitmap_similarity(im, bm)))
out.write(stream)
out.write(stream[:bytes_left])
if bytes_left <= 0:
out.write(bytes(s.done()))
break
out.write(bytes(s.done()))
if __name__ == "__main__":