393 words
2 minutes
TCTT25 Misc: Die With a Smile
Die With a Smile (300 pts) - Miscellaneous Write-up
โจทย์
Before he passed away, an old man — once a brilliant AI vibe-coder — dumped the fragmented memories of his friendships into a single image.
Within this mosaic of faces, he hid his eternal secret, encoded not in pixels, but in smiles and silence.
In this strange new era, emotion is encryption — and only by teaching your model to truly understand a smile can you hope to unlock what he left behind.
Can your classifier uncover the truth… or will it be lost to grayscale eternity?
Hint:
- It is the public dataset
- The QR isn’t broken. You are, until the classifier works.
ดาวน์โหลดไฟล์
| ไฟล์ | ดาวน์โหลด |
|---|---|
| Mosaic Image | 📦 GitHub Folder |
ข้อสังเกต
- ภาพโมเสกมีขนาด 2112×2112px แบ่งได้ลงตัวเป็น 33×33 ช่อง (QR Version 3)
- แต่ละ tile (≈62×62 พิกเซล) คือใบหน้าใน dataset CelebA
- Attribute สำคัญคือ
Smiling(index 31) ของ CelebA - หากใช้ threshold ค่าเฉลี่ยความสว่างอย่างเดียว ผล QR อาจเพี้ยน
- ต้องใช้ Smile classifier ที่แม่น (เช่น ResNet18 ที่ finetune 2-class not-smile/smile)
แนวคิดการแก้โจทย์
- แบ่งภาพโมเสก ออกเป็น 33×33 tiles
- โหลดโมเดล ResNet18 (2-class) ที่ train บน CelebA (label
Smiling)- output = 0 (not-smile), 1 (smile)
- Preprocess tile → resize 128×128 → normalize
- Predict smile:
- ถ้า
pred=1→ ให้ tile เป็น สีดำ - ถ้า
pred=0→ ให้ tile เป็น สีขาว
- ถ้า
- รวมผลลัพธ์ → ขยายภาพแบบ nearest → จะได้ QR Code สมบูรณ์
- สแกน QR Code → อ่าน flag
ขั้นตอน Implementation
Step 1: แบ่ง Mosaic เป็น Tiles
from PIL import Imageimport numpy as np
img = Image.open('mosaic.png')tiles = []tile_size = 64 # 2112 / 33 ≈ 64
for row in range(33): for col in range(33): x = col * tile_size y = row * tile_size tile = img.crop((x, y, x + tile_size, y + tile_size)) tiles.append(tile)Step 2: โหลด Smile Classifier
import torchimport torchvision.transforms as transformsfrom torchvision.models import resnet18
# Load pretrained model (fine-tuned on CelebA Smiling)model = resnet18(pretrained=False)model.fc = torch.nn.Linear(512, 2) # 2 classesmodel.load_state_dict(torch.load('smile_classifier.pth'))model.eval()
transform = transforms.Compose([ transforms.Resize((128, 128)), transforms.ToTensor(), transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])])Step 3: Predict และสร้าง QR
qr_data = []
for tile in tiles: input_tensor = transform(tile).unsqueeze(0) with torch.no_grad(): output = model(input_tensor) pred = output.argmax(1).item()
# smile=1 → black, not-smile=0 → white qr_data.append(0 if pred == 1 else 255)
# Reshape to 33x33qr_array = np.array(qr_data).reshape(33, 33).astype(np.uint8)qr_image = Image.fromarray(qr_array, mode='L')qr_image = qr_image.resize((330, 330), Image.NEAREST)qr_image.save('qr_code.png')Step 4: สแกน QR Code
import cv2from pyzbar.pyzbar import decode
qr = cv2.imread('qr_code.png')data = decode(qr)print(data[0].data.decode())Dataset
CelebA (CelebFaces Attributes Dataset)
- 200K+ celebrity face images
- 40 binary attributes including
Smiling - Public dataset สำหรับ train smile classifier
Tools ที่ใช้
| Tool | วัตถุประสงค์ |
|---|---|
| PyTorch | Deep learning model |
| ResNet18 | CNN architecture |
| PIL/Pillow | Image processing |
| pyzbar | QR code decoding |
ผลลัพธ์
สแกน reconstructed QR code เพื่อรับ flag
Credits
Writeup by netw0rk7 | Original Repo
TCTT25 Misc: Die With a Smile
https://blog.lukkid.dev/posts/tctt25-misc-die-with-a-smile/