import cv2
import numpy as np
from reedsolo import RSCodec
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
print("r=ASCII read | p=RS decode | wasd=move | WASD=fast | g/h=zoom | G/H=fast | i/o=rotate | q=quit")
# ==========================================================
# BASE POINTS
# ==========================================================
base_points = [
(668, 387),
(668, 433),
(668, 477),
(668, 522),
(668, 567),
(668, 610),
(668, 655),
(668, 700),
(668, 745)
]
# ==========================================================
# BUILD COLUMNS
# ==========================================================
columns = []
# 8 columns (45 px spacing)
for i in range(8):
columns.append([
(x + i * 45, y)
for (x, y) in base_points
])
# 3 columns (42 px spacing)
offset = 8 * 45
for i in range(3):
columns.append([
(x + offset + i * 42, y)
for (x, y) in base_points
])
# 5 columns (45 px spacing)
offset += 3 * 42
for i in range(5):
columns.append([
(x + offset + i * 45, y)
for (x, y) in base_points
])
# ==========================================================
# FINE CALIBRATION
# ==========================================================
# Column 9 slightly left
columns[8] = [
(x - 1, y)
for (x, y) in columns[8]
]
# Columns 11-16 slightly right
for i in range(10, min(16, len(columns))):
shift = 2
# Column 12 extra shift
if i == 11:
shift = 4
columns[i] = [
(x + shift, y)
for (x, y) in columns[i]
]
# ==========================================================
# SETTINGS
# ==========================================================
THRESHOLD = 100
poly_pts = np.array([
[520, 317],
[1408, 317],
[1408, 811],
[593, 811],
[516, 741]
], np.float32)
# ==========================================================
# TRANSFORM VARIABLES
# ==========================================================
offset_x = 0
offset_y = 0
scale = 1.0
rotation = 0.0
# ==========================================================
# OVERLAY DATA
# ==========================================================
last_matrix = None
last_ascii = ""
last_status = ""
last_color = (255, 255, 255)
# ==========================================================
# MAIN LOOP
# ==========================================================
while True:
ret, frame = cap.read()
if not ret:
break
h, w = frame.shape[:2]
cx = w // 2
cy = h // 2
display = frame.copy()
# ======================================================
# TRANSFORM FUNCTION
# ======================================================
def transform(x, y):
# Scale around center
x0 = (x - cx) * scale
y0 = (y - cy) * scale
# Rotate
xr = (
x0 * np.cos(rotation)
- y0 * np.sin(rotation)
)
yr = (
x0 * np.sin(rotation)
+ y0 * np.cos(rotation)
)
# Translate
x2 = cx + xr + offset_x
y2 = cy + yr + offset_y
return int(x2), int(y2)
# ======================================================
# DRAW POLYGON
# ======================================================
poly_scaled = np.array([
transform(x, y)
for (x, y) in poly_pts
])
cv2.polylines(
display,
[poly_scaled.reshape((-1, 1, 2))],
True,
(0, 255, 0),
2
)
# ======================================================
# DRAW SAMPLE POINTS
# ======================================================
shifted_columns = []
for col in columns:
new_col = []
for (x, y) in col:
tx, ty = transform(x, y)
new_col.append((tx, ty))
cv2.circle(
display,
(tx, ty),
5,
(0, 0, 255),
-1
)
shifted_columns.append(new_col)
# ======================================================
# INPUT
# ======================================================
key = cv2.waitKey(1) & 0xFF
# ------------------------------------------------------
# MOVE
# ------------------------------------------------------
if key == ord('w'):
offset_y -= 1
elif key == ord('s'):
offset_y += 1
elif key == ord('a'):
offset_x -= 1
elif key == ord('d'):
offset_x += 1
# ------------------------------------------------------
# FAST MOVE
# ------------------------------------------------------
elif key == ord('W'):
offset_y -= 5
elif key == ord('S'):
offset_y += 5
elif key == ord('A'):
offset_x -= 5
elif key == ord('D'):
offset_x += 5
# ------------------------------------------------------
# ZOOM
# ------------------------------------------------------
elif key == ord('g'):
scale *= 0.995
elif key == ord('h'):
scale /= 0.995
# ------------------------------------------------------
# FAST ZOOM
# ------------------------------------------------------
elif key == ord('G'):
scale *= 0.97
elif key == ord('H'):
scale /= 0.97
# ------------------------------------------------------
# ROTATION
# ------------------------------------------------------
elif key == ord('i'):
rotation -= 0.002
elif key == ord('o'):
rotation += 0.002
# ------------------------------------------------------
# FAST ROTATION
# ------------------------------------------------------
elif key == ord('I'):
rotation -= 0.01
elif key == ord('O'):
rotation += 0.01
# ======================================================
# ASCII CARD MODE
# ======================================================
elif key == ord('r'):
matrix = []
ascii_chars = []
all_ok = True
for col in shifted_columns:
bits = []
for (x, y) in col:
if 0 <= x < w and 0 <= y < h:
b, g, r_pix = frame[y, x]
gray = int(
0.299 * r_pix +
0.587 * g +
0.114 * b
)
bit = 1 if gray < THRESHOLD else 0
bits.append(bit)
else:
bits.append(0)
matrix.append(bits)
# row 0 = parity
# row 1 = LSB
parity = bits[0]
data_bits = bits[1:]
value = 0
for i, b in enumerate(data_bits):
value |= (b << i)
# even parity
ones = sum(data_bits)
expected = 1 if ones % 2 == 0 else 0
if parity != expected:
all_ok = False
ascii_chars.append(
chr(33 + (value % 94))
)
last_matrix = matrix
last_ascii = ''.join(ascii_chars)
if all_ok:
last_status = "PARITY OK"
last_color = (0, 255, 0)
else:
last_status = "PARITY ERROR"
last_color = (0, 0, 255)
print("\nASCII:", last_ascii)
# ======================================================
# REED SOLOMON MODE
# ======================================================
elif key == ord('p'):
rs_values = []
rs_matrix = []
print("\n--- REED SOLOMON READ ---")
for col_idx, col in enumerate(shifted_columns):
bits = []
for (x, y) in col:
if 0 <= x < w and 0 <= y < h:
b, g, r_pix = frame[y, x]
gray = int(
0.299 * r_pix +
0.587 * g +
0.114 * b
)
bit = 1 if gray < THRESHOLD else 0
bits.append(bit)
else:
bits.append(0)
rs_matrix.append(bits)
# row 0 = parity
# rows 1-8 = data
data_bits = bits[1:]
# LSB at bottom
reversed_bits = list(reversed(data_bits))
value = 0
for i, b in enumerate(reversed_bits):
value |= (b << i)
rs_values.append(value)
print(f"Column {col_idx}: {value}")
print("RS Values:", rs_values)
# --------------------------------------------------
# REED SOLOMON DECODE
# --------------------------------------------------
try:
rsc = RSCodec(6)
decoded = rsc.decode(
bytearray(rs_values)
)
decoded_bytes = decoded[0]
decoded_text = decoded_bytes.decode(
'utf-8',
errors='replace'
)
print("Decoded text:", decoded_text)
last_matrix = rs_matrix
last_ascii = decoded_text
last_status = "RS DECODE OK"
last_color = (0, 255, 255)
except Exception as e:
print("RS decode failed:", e)
last_matrix = rs_matrix
last_ascii = "RS DECODE FAILED"
last_status = "RS ERROR"
last_color = (0, 0, 255)
# ======================================================
# QUIT
# ======================================================
elif key == ord('q'):
break
# ======================================================
# OVERLAY
# ======================================================
if last_matrix is not None:
cell = 18
rows = 9
cols = len(last_matrix)
tx = w - cols * cell - 20
ty = 20
# Background
cv2.rectangle(
display,
(tx - 10, ty - 10),
(tx + cols * cell + 10,
ty + rows * cell + 60),
(0, 0, 0),
-1
)
# Matrix
for c in range(cols):
for r in range(rows):
val = last_matrix[c][r]
color = (
(0, 255, 0)
if val else
(100, 100, 100)
)
cv2.putText(
display,
str(val),
(tx + c * cell,
ty + r * cell + 14),
cv2.FONT_HERSHEY_SIMPLEX,
0.5,
color,
1
)
# Result text
cv2.putText(
display,
last_ascii,
(tx, ty + rows * cell + 20),
cv2.FONT_HERSHEY_SIMPLEX,
0.7,
(255, 255, 255),
2
)
# Status
cv2.putText(
display,
last_status,
(tx, ty + rows * cell + 45),
cv2.FONT_HERSHEY_SIMPLEX,
0.7,
last_color,
2
)
cv2.imshow("Camera", display)
cap.release()
cv2.destroyAllWindows()
Comments