import pathlib
import sys
import cv2
import numpy as np
MAX_BITS = 6
EXCLUDE_RANGE = 4
BITMAP_CASES = [(16, 16), (13, 9), (40, 5), (8, 8), (65, 3)]
SHIFT_CASES = [(11, 7, 0, 0), (11, 7, 3, 2), (11, 7, -4, -1), (11, 7, 10, 6), (70, 5, -1, 4)]
ALIGN_CASES = [(96, 72, 4, (5, -3)), (96, 72, 4, (-7, 6)), (64, 64, 3, (2, 2))]
SHRINK_CASES = [(8, 8), (9, 7), (1, 1), (65, 3)]
def downsample(src):
height, width = src.shape
return np.ascontiguousarray(src[: height // 2 * 2 : 2, : width // 2 * 2 : 2])
def scene(width, height, seed):
rng = np.random.default_rng(seed)
total = np.zeros((height, width), np.float32)
for cell, weight in ((16, 0.45), (4, 0.25), (2, 0.15), (1, 0.15)):
coarse = rng.random((max(height // cell, 2), max(width // cell, 2)), dtype=np.float32)
total += weight * cv2.resize(coarse, (width, height), interpolation=cv2.INTER_CUBIC)
return np.clip(total * 255.0, 0, 255).astype(np.uint8)
def windows(width, height, offset, seed):
margin = 32
whole = scene(width + 2 * margin, height + 2 * margin, seed)
dx, dy = offset
def view(ox, oy):
return np.ascontiguousarray(
whole[margin + oy : margin + oy + height, margin + ox : margin + ox + width]
)
return view(0, 0), view(dx, dy)
def hexed(array):
return array.tobytes().hex()
def bits(mask):
return "".join("1" if value else "0" for value in mask.flatten())
def main():
aligner = cv2.createAlignMTB(max_bits=MAX_BITS, exclude_range=EXCLUDE_RANGE, cut=True)
out = []
for seed, (width, height) in enumerate(BITMAP_CASES):
source = (
np.full((height, width), 255, np.uint8)
if (width, height) == (8, 8)
else scene(width, height, seed)
)
tb, eb = aligner.computeBitmaps(source)
out.append(
f"CASE bitmaps {width} {height} {EXCLUDE_RANGE}\n"
f"IN {hexed(source)}\nTB {bits(tb)}\nEB {bits(eb)}"
)
for seed, (width, height, dx, dy) in enumerate(SHIFT_CASES, start=100):
source = scene(width, height, seed)
moved = aligner.shiftMat(source, (dx, dy))
out.append(
f"CASE shift {width} {height} {dx} {dy}\n"
f"IN {hexed(source)}\nOUT {hexed(moved)}"
)
for seed, (width, height) in enumerate(SHRINK_CASES, start=150):
source = scene(width, height, seed)
shrunk = downsample(source)
out.append(
f"CASE shrink {width} {height} {shrunk.shape[1]} {shrunk.shape[0]}\n"
f"IN {hexed(source)}\nOUT {hexed(shrunk)}"
)
for seed, (width, height, bits_, offset) in enumerate(ALIGN_CASES, start=200):
reference, target = windows(width, height, offset, seed)
sized = cv2.createAlignMTB(max_bits=bits_, exclude_range=EXCLUDE_RANGE, cut=True)
dx, dy = sized.calculateShift(reference, target)
out.append(
f"CASE align {width} {height} {bits_} {EXCLUDE_RANGE}\n"
f"REF {hexed(reference)}\nTGT {hexed(target)}\nOUT {dx} {dy}"
)
if (dx, dy) != offset:
print(
f"note: OpenCV read {(dx, dy)} where the scene was built at {offset}",
file=sys.stderr,
)
destination = pathlib.Path(__file__).parent / "opencv.txt"
destination.write_text(
f"# generated by generate.py against OpenCV {cv2.__version__}\n" + "\n".join(out) + "\n"
)
print(f"{len(out)} cases -> {destination}")
if __name__ == "__main__":
main()