diff options
author | Anthony Wang | 2024-04-21 01:15:52 -0400 |
---|---|---|
committer | Anthony Wang | 2024-04-21 01:15:52 -0400 |
commit | cef9bab49ad9f303978858b2824ab0862c0df689 (patch) | |
tree | 6cb48d326c9ee5d3d5f0c3ac71ac75077340109a | |
parent | 762d99dc02066555f723b9537c65330f8bf9d10f (diff) |
Implement ECC, webcam capture
-rw-r--r-- | decoder.py | 40 | ||||
-rw-r--r-- | encoder.py | 84 | ||||
-rw-r--r-- | requirements.txt | 1 |
3 files changed, 108 insertions, 17 deletions
diff --git a/decoder.py b/decoder.py new file mode 100644 index 0000000..357db39 --- /dev/null +++ b/decoder.py @@ -0,0 +1,40 @@ +import argparse +import cv2 +import numpy as np +from creedsolo import RSCodec +from raptorq import Decoder + +parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument("file", help="output file for decoded data") +parser.add_argument("--len", help="number of bytes to decode", default=2**16, type=int) +parser.add_argument("--height", help="grid height", default=100, type=int) +parser.add_argument("--width", help="grid width", default=100, type=int) +parser.add_argument("--fps", help="framerate", default=30, type=int) +parser.add_argument("--level", help="error correction level", default=0.1, type=float) +args = parser.parse_args() + +cheight = args.height // 10 +cwidth = args.width // 10 +frame_size = args.height * args.width - 4 * cheight * cwidth +rs_size = int(frame_size * (1 - args.level)) + +rsc = RSCodec(frame_size - rs_size) +raptor_decoder = Decoder.with_defaults(args.size, rs_size) +data = None +cap = cv2.VideoCapture(0) +while data is None: + ret, frame = cap.read() + if not ret: + continue + frame_full = decode(frame) # TODO + frame_data = np.concatenate( + ( + frame_full[:cheight, cwidth : args.width - cwidth].flatten(), + frame_full[cheight : args.height - cheight].flatten(), + frame_full[args.heigth - cheight, cwidth : args.width - cwidth].flatten(), + ) + ) + data = raptor_decoder.decode(rsc.decode(frame_data)) +with open(args.file, "wb") as f: + f.write(data) +cap.release() @@ -1,14 +1,38 @@ +import argparse import sys import numpy as np +from creedsolo import RSCodec from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout from PyQt6.QtGui import QPixmap from PyQt6.QtCore import QTimer from PIL import Image, ImageQt +from raptorq import Encoder -fps = 30 -h = 200 -w = 200 -c = 0 +parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument("file", help="output file for decoded data") +parser.add_argument("--height", help="grid height", default=100, type=int) +parser.add_argument("--width", help="grid width", default=100, type=int) +parser.add_argument("--fps", help="framerate", default=30, type=int) +parser.add_argument("--level", help="error correction level", default=0.1, type=float) +args = parser.parse_args() + + +cheight = args.height // 10 +cwidth = args.width // 10 +midwidth = args.width - 2 * cwidth +frame_size = args.height * args.width - 4 * cheight * cwidth +# reedsolo breaks message into 255-byte chunks +# raptorq can add up to 4 extra bytes +rs_size = frame_size - int((frame_size + 254) / 255) * int(args.level * 255) - 4 + +with open(args.file, "rb") as f: + data = f.read() +rsc = RSCodec(int(args.level * 255)) +encoder = Encoder.with_defaults(data, rs_size) +packets = encoder.get_encoded_packets(int(len(data) / rs_size * args.level)) +print("Data length:", len(data)) +print("Packets:", len(packets)) +input("Seizure warning!") class EncoderWidget(QWidget): @@ -16,7 +40,8 @@ class EncoderWidget(QWidget): super().__init__() self.timer = QTimer(self) self.timer.timeout.connect(self.update) - self.timer.start(1000 // fps) + self.timer.start(1000 // args.fps) + self.idx = 0 self.label = QLabel(self) layout = QVBoxLayout(self) layout.addWidget(self.label) @@ -25,20 +50,45 @@ class EncoderWidget(QWidget): self.showFullScreen() def update(self): - global c - if c == 0: - array = np.random.randint(0, 256, (h, w, 3)) - c = 1 - else: - array = np.zeros((h, w, 3)) - c = 0 - img = Image.fromarray(array.astype(np.uint8), mode="RGB") + frame_data = np.array(rsc.encode(packets[self.idx])) + # Pad frame to fit frame_size since raptorq might not add 4 bytes + frame_data = np.pad(frame_data, (0, frame_size - len(frame_data))) + self.idx = (self.idx + 1) % len(packets) + frame = np.concatenate( + ( + np.concatenate( + ( + np.zeros((cheight, cwidth), dtype=np.uint8), # TODO + frame_data[: cheight * midwidth].reshape((cheight, midwidth)), + np.zeros((cheight, cwidth), dtype=np.uint8), # TODO + ), + axis=1, + ), + frame_data[ + cheight * midwidth : frame_size - cheight * midwidth + ].reshape((args.height - 2 * cheight, args.width)), + np.concatenate( + ( + np.zeros((cheight, cwidth), dtype=np.uint8), # TODO + frame_data[frame_size - cheight * midwidth :].reshape( + (cheight, midwidth) + ), + np.zeros((cheight, cwidth), dtype=np.uint8), # TODO + ), + axis=1, + ), + ) + ) + color_frame = np.stack( + ((frame & 0x03) << 6, (frame >> 2 & 0x07) << 5, (frame >> 5 & 0x7) << 5), + axis=-1, + ) + img = Image.fromarray(color_frame) qt_img = ImageQt.ImageQt(img) pixmap = QPixmap.fromImage(qt_img).scaled(self.size()) self.label.setPixmap(pixmap) -if __name__ == "__main__": - app = QApplication(sys.argv) - widget = EncoderWidget() - sys.exit(app.exec()) +app = QApplication([]) +widget = EncoderWidget() +sys.exit(app.exec()) diff --git a/requirements.txt b/requirements.txt index e1ef11e..9543501 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ Cython==3.0.10 numpy==1.26.4 +opencv-python==4.9.0.80 pillow==10.3.0 PyQt6==6.6.1 PyQt6-Qt6==6.6.3 |