liqUID glASS - smileyCTF 2025
画像を変形するwasmと、変形したQRコードの画像が与えられる
opencvのremapを使って逆向きに変形すると元の画像が得られる
code:python
import numpy as np
import cv2
def unwrap(warped_rgba: np.ndarray) -> np.ndarray:
H, W = warped_rgba.shape:2 x = np.arange(W, dtype=np.float32)
y = np.arange(H, dtype=np.float32)
xv, yv = np.meshgrid(x, y)
# ... (色調補正と逆ワープマップ計算の部分は変更なし) ...
u = xv / W
v = yv / H
du = u - 0.5
dv = v - 0.5
r = np.hypot(du, dv)
# ---- 色調補正の逆算 (変更なし) ----
r_half = r * 0.5
base = 1.0 - r_half
gain_r = base * 0.9 + r_half * 0.8
gain_g = base + r_half * 0.95
gain_b = base * 0.95 + r_half * 0.85
warped_f = warped_rgba.astype(np.float32)
sampled_f = warped_f.copy()
sampled_f:3 /= gains + 1e-8
np.clip(sampled_f:3, 0, 255, out=sampled_f:3)
# ---- 逆ワープマップの高速計算 (変更なし) ----
S = 0.1 * np.sin(25.0 * u + 20.0 * v)
C = 0.1 * np.cos(20.0 * u + 25.0 * v)
u_p = np.clip(u + S + du * r * 0.5, 0.0, 1.0)
v_p = np.clip(v + C + dv * r * 0.5, 0.0, 1.0)
map_x_fwd = (u_p * (W - 1))
map_y_fwd = (v_p * (H - 1))
map_x_inv_sparse = np.full((H, W), -1.0, dtype=np.float32)
map_y_inv_sparse = np.full((H, W), -1.0, dtype=np.float32)
sx_int = np.rint(map_x_fwd).astype(np.int32)
sy_int = np.rint(map_y_fwd).astype(np.int32)
valid_mask = (sx_int >= 0) & (sx_int < W) & (sy_int >= 0) & (sy_int < H)
# ---- 穴埋め (Hole Filling) - ここからが修正箇所 ----
# 穴(-1.0)となっている部分を特定するマスクを作成 (0:有効、1:穴)
hole_mask = (map_x_inv_sparse == -1.0).astype(np.uint8)
# # cv2.inpaintを使用して穴を埋める
# # 第3引数は修復に使う近傍の半径、第4引数はアルゴリズム
# map_x_inv = cv2.inpaint(map_x_inv_sparse, hole_mask, 3, cv2.INPAINT_NS)
# map_y_inv = cv2.inpaint(map_y_inv_sparse, hole_mask, 3, cv2.INPAINT_NS)
# ---- 逆ワープの適用 (変更なし) ----
unwarped_f = cv2.remap(sampled_f,
map_x_inv_sparse.astype(np.float32),
map_y_inv_sparse.astype(np.float32),
interpolation=cv2.INTER_LINEAR,
borderMode=cv2.BORDER_REPLICATE)
unwarped_f3 = 255
return unwarped_f.astype(np.uint8)
wrapped = cv2.cvtColor(cv2.imread('liquidglass.png', cv2.IMREAD_UNCHANGED), cv2.COLOR_BGRA2RGBA)
restored = unwrap(wrapped)
cv2.imwrite('restored.png', cv2.cvtColor(restored, cv2.COLOR_RGBA2BGRA))
https://scrapbox.io/files/687530ed6978538fe9c4a8fe.png