딥페이크 변조 방지를 위한 Face Swap Defender 구현 및 오픈소스 공유

안녕하세요,
오키 회원 여러분! 👋 잡학다식 개발자 gohard Lab 입니다.
요즘 생성형 AI 기술이 급격히 발전하면서, SNS 사진 몇 장만으로도 고화질 페이스 스왑(Face Swap)을 구현하는 기술이 대중화되었습니다. 기술의 발전은 흥미롭지만, 내가 올려둔 원본 사진이 무단 도용되어 딥페이크 합성에 악용될 수 있다는 보안적 위험도 커지고 있죠. 😞
"사람의 눈에는 전혀 티가 나지 않으면서, AI 모델이 내 얼굴을 합성하려고 할 때 랜드마크 추적을 교란시키는 보이지 않는 방어막을 칠 수는 없을까?"라는 고민에서 이 프로젝트를 시작하게 되었습니다. 🔍
보통 이런 AI/CV 프로젝트는 로컬 PC에서 GPU 환경이나 CUDA 라이브러리를 세팅하다가 버전을 맞추지 못해 에러가 나기 쉽습니다. 그래서 오키 회원분들이 별도의 환경 설정 스트레스 없이 웹상에서 버튼 하나로 즉시 실행해 보실 수 있도록 구글 코랩(Google Colab) 실행 환경으로 전체 코드를 완성해 두었습니다! 🚀
오늘은 이 프로젝트의 핵심 알고리즘과 함께, 주파수 도메인 포렌식 워터마킹 구현 노하우를 공유해 드립니다.
💻 핵심 소스코드 (Adversarial Perturbation & DCT Watermark)
얼굴 윤곽선에 적응형 노이즈를 주입하고, 주파수 도메인(DCT)에 은닉 워터마크를 각인하는 핵심 방어 모듈입니다. 🛠️
import cv2
import numpy as np
def apply_stealth_defense(img, base_epsilon=0.01, secret_text="PROVENANCE"):
# 1. 구조적 엣지(윤곽선) 추출을 위한 소벨 필터링
img_float = img.astype(np.float32) / 255.0
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
grad_x = cv2.Sobel(gray, cv2.CV_32F, 1, 0, ksize=3)
grad_y = cv2.Sobel(gray, cv2.CV_32F, 0, 1, ksize=3)
magnitude = cv2.magnitude(grad_x, grad_y)
edge_mask = cv2.normalize(magnitude, None, 0.0, 1.0, cv2.NORM_MINMAX)
edge_mask = np.expand_dims(edge_mask, axis=2)
# 2. 적대적 교란 파형 생성 및 적응형 노이즈 주입
h, w, c = img.shape
x_coords, y_coords = np.meshgrid(np.arange(w), np.arange(h))
carrier_wave = np.sin(x_coords * 0.5) * np.cos(y_coords * 0.5)
carrier_wave = np.repeat(np.expand_dims(carrier_wave, axis=2), c, axis=2)
adaptive_noise = base_epsilon * carrier_wave * (1.0 + edge_mask * 2.0)
protected_float = np.clip(img_float + adaptive_noise, 0.0, 1.0)
protected_img = (protected_float * 255.0).astype(np.uint8)
# 3. 주파수 도메인(DCT) 포렌식 워터마크 각인
ycrcb = cv2.cvtColor(protected_img, cv2.COLOR_BGR2YCrCb)
y_channel, cr, cb = cv2.split(ycrcb)
y_float = y_channel.astype(np.float32)
dct_coeffs = cv2.dct(y_float)
text_mask = np.zeros((h, w), dtype=np.uint8)
cv2.putText(text_mask, secret_text, (int(w*0.3), int(h*0.5)),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, 255, 1, cv2.LINE_AA)
dct_coeffs[text_mask > 0] += 15.0
idct_y = np.clip(cv2.idct(dct_coeffs), 0, 255).astype(np.uint8)
merged = cv2.merge([idct_y, cr, cb])
return cv2.cvtColor(merged, cv2.COLOR_YCrCb2BGR)🔑 주요 함수 및 핵심 연산 개념
코드에 쓰인 핵심 OpenCV 함수들의 기본 원리입니다. ⚙️
cv2.Sobel: 이미지의 명암 변화율(경사도)을 계산하여 윤곽선(Edge)을 찾아내는 필터입니다. X축과 Y축 방향의 미분을 수행하여 AI가 얼굴 특징점을 추출하는 주요 구조를 포착합니다.
cv2.dct 및 cv2.idct: 이산 코사인 변환(Discrete Cosine Transform) 및 역변환 함수입니다. 이미지 공간을 주파수 도메인으로 축 변환하여 시각적 손실을 최소화하면서 보안 데이터를 은닉할 때 활용됩니다.
np.clip: 지정한 최소값과 최대값 범위를 벗어나는 배열 요소를 강제로 고정하는 함수입니다. 주파수 변환 계산 중 발생할 수 있는 데이터 오버플로우나 픽셀 붕괴 현상을 예방합니다.
💡 실전 노이즈 은닉 및 보안 꿀팁 (파이썬 예제 포함)
1️⃣ 구조적 엣지 기반 적응형 노이즈 주입 (Adaptive Noise) : 이미지 전체에 고르게 노이즈를 넣으면 매끄러운 볼이나 이마 표면에 자글자글한 격자무늬가 눈에 띄게 드러납니다. 이를 우회하기 위해 소벨 필터로 추출한 엣지 마스크(edge_mask)를 활용합니다. 눈, 코, 입 주변이나 경계선처럼 복잡한 텍스처 영역에는 노이즈 강도를 높이고, 평탄한 피부 영역에는 강도를 낮춰 사람의 눈에는 원본과 똑같이 보이면서 AI 모델만 교란시킵니다. 🔥
Python
# [핵심 예제 1] 엣지 가중치를 활용한 적응형 노이즈 주입 예시
import cv2
import numpy as np
# 엣지 마스크 추출 (0.0 ~ 1.0 범위 정규화)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
grad_x = cv2.Sobel(gray, cv2.CV_32F, 1, 0, ksize=3)
grad_y = cv2.Sobel(gray, cv2.CV_32F, 0, 1, ksize=3)
magnitude = cv2.magnitude(grad_x, grad_y)
edge_mask = cv2.normalize(magnitude, None, 0.0, 1.0, cv2.NORM_MINMAX)
edge_mask = np.expand_dims(edge_mask, axis=2)
# 평탄한 영역은 노이즈 최소화, 엣지 영역만 3배 가중치 주입
base_noise = np.random.normal(0, 0.01, image.shape)
adaptive_noise = base_noise * (1.0 + edge_mask * 2.0)
2️⃣ 소셜 미디어 압축(JPEG)을 견디는 주파수 도메인 각인 : 일반적인 단순 픽셀 워터마크는 인스타그램이나 페이스북에 사진을 올릴 때 수행되는 이미지 압축 공격에 쉽게 파괴됩니다. YCrCb 색상 공간의 Y(휘도) 채널을 추출하여 주파수 변환(DCT)을 거친 뒤, 중저주파 계수 영역에 신호를 각인하고 np.clip으로 안전 범위를 고정하면 압축 후에도 데이터가 유지됩니다. ✅
Python
# [핵심 예제 2] 주파수 계수 각인 및 오버플로우 방지 예시
import cv2
import numpy as np
# Y 채널 분리 및 DCT 주파수 도메인 변환
ycrcb = cv2.cvtColor(image, cv2.COLOR_BGR2YCrCb)
y_channel, cr, cb = cv2.split(ycrcb)
dct_coeffs = cv2.dct(y_channel.astype(np.float32))
# 특정 중저주파 계수 위치에 보안 신호 각인
dct_coeffs[50:60, 50:60] += 15.0
# 역변환(IDCT) 및 안전한 0~255 범위 클리핑
idct_y = cv2.idct(dct_coeffs)
idct_y_safe = np.clip(idct_y, 0, 255).astype(np.uint8)
final_image = cv2.cvtColor(cv2.merge([idct_y_safe, cr, cb]), cv2.COLOR_YCrCb2BGR)🔗 소스코드 및 시연 영상 안내
로컬 설치 에러 걱정 없이 바로 테스트해 보실 수 있도록 구글 코랩 실행 링크를 준비했습니다. 안심하고 접속하셔서 본인 사진으로 스텔스 방어막을 직접 시험해 보세요!
적용해 본 분들의 후기나, 주파수 포렌식 알고리즘에 대해 막히는 부분 및 더 좋은 개선 아이디어가 있다면 편하게 댓글 남겨주시기 바랍니다. 💬
이 코드가 실제로 굴러가는 꿀잼 시연 영상은 유튜브에서 확인해 보세요! 🎬
📌 구글 코랩 실시간 실행 링크: https://colab.research.google.com/drive/1i_ABlfu_9ABN675XIzSr5QxxrZKU9sDI?usp=sharing
📌 GitHub 원본 소스코드 저장소: https://github.com/gohard-lab/face_swap_defender
📺 워드프레스: https://gohard.pe.kr/
🎬 실제 프로그램 동작 및 시연 영상: https://youtu.be/V0QOYNG33ro
📌 블로그: https://gohard.pe.kr/
감사합니다!