이미지 복원 — 저해상도·옛 사진을 AI로 확대해 일반 확대와 비교한 연구 결과
Real-ESRGAN 계열 모델을 CPU에서 돌려 저해상도 사진을 확대하고, 같은 배율의 일반 확대와 슬라이더로 비교하는 PoC를 만들어 봤습니다.
개발 개요
옛 사진이나 해상도가 낮은 사진을 AI로 2배 확대했을 때 같은 배율의 일반 확대(바이큐빅)보다 나은지를 눈으로 확인하는 화면을 만들었습니다. 샘플 8장과 5초 풍경 영상 1건을 미리 처리해 보여 주고, 원하는 이미지를 올려 바로 복원하고 결과를 내려받는 기능도 넣었습니다.
모델은 모두 공개된 사전학습 가중치를 사용했고 별도 학습은 하지 않았습니다. GPU 없이 CPU에서 돌아가는 구성이라 일반 PC에서도 시연할 수 있습니다.
AI를 활용한 설계 및 구현 과정
Claude Code에 조사, 요구사항, 설계, 구현, 테스트, 리뷰 역할을 나눠 맡기고, 단계마다 검토를 통과해야 다음으로 넘어가게 했습니다. 방향과 범위는 직접 정했고 결과도 직접 확인해 결정했습니다. 핵심은 결과를 보기 전에 판정 기준을 먼저 정해 해시로 고정(사전 등록)하고, 한 번도 보지 않은 보류셋으로 최종 판정을 딱 한 번 한 것입니다. 개발셋에서 설정을 최대 4번 바꿔 가며 개선했습니다.
잘 나온 결과만 골라 보여 주는 것을 막으려고 이미지는 개발셋 15장과 보류셋 15장으로 나누었습니다. 설정을 고치며 개선하는 일은 개발셋에서만 했고, 보류셋은 열지 않고 두었다가 최종 판정에만 사용했습니다.
사용한 기술
- 복원 모델: Real-ESRGAN(x4plus, 이후 realesr-general-x4v3)으로 확대하고, 흑백 사진은 DeOldify(Stable)로 컬러화합니다. GFPGAN v1.3 얼굴 복원도 단계로 넣었지만 최종 설정에서는 껐습니다. 전부 사전학습 가중치이고 학습은 하지 않았습니다. 실행은 Python 3.10과 CPU 전용 PyTorch입니다.
- 잡음 강도 조절: general-x4v3는 잡음 제거가 강하면 옛 인쇄물의 망점까지 지워서 매끈해집니다. 잡음을 강하게 지우는 모델과 약하게 지우는 짝 모델(wdn)을 섞는 비율(DNI 보간)을 0~1로 조절해 질감을 얼마나 남길지 바꿔 보았고, 최종 설정은 0.25입니다. 값이 낮을수록 질감과 필름 그레인이 남고, 높을수록 매끈해집니다.
- 단계 분리: 모델마다 라이브러리 버전이 달라서 확대·얼굴·컬러화를 별도 프로세스로 나누고 PNG 파일로 주고받게 했습니다. 모델 가중치는 SHA256으로 검증합니다.
- 비교 방식: 같은 배율의 일반 확대(바이큐빅)를 기준선으로 두고, 비교 이미지는 좌우 위치를 섞어 어느 쪽이 AI 결과인지 모르게 AI 판정자에게 보여 줬습니다.
- 데모: Streamlit 화면이고 모델 실행은 서브프로세스로 부릅니다. 업로드는 형식·용량·해상도를 검사하고 과도하게 큰 이미지(폭탄 이미지)는 막으며, 처리 결과는 임시 폴더에 두었다가 새 업로드나 종료 때 지웁니다. 샘플의 얼굴은 검출 뒤 블러를 씌우고 재검출로 빠진 곳이 없는지 확인합니다. 영상은 OpenCV로 프레임마다 처리해 H.264로 묶었습니다.
- 검증 도구: pytest(900건 이상)와 Streamlit AppTest, 브라우저 자동화(Playwright)로 화면을 확인했습니다.
구현 결과와 사용법
화면은 "이미지 복원" 하나이고 모드는 "샘플 / 업로드"입니다. 안내문·배지·탭 같은 보조 서술은 모두 빼고 데이터와 컨트롤만 남겼습니다. 샘플 카드에서 "열기"를 누르면 상세 화면이 열리고, 왼쪽 일반 확대와 오른쪽 AI 복원 결과를 슬라이더로 비교합니다.
비교 방식은 슬라이더 외에 나란히, 차이 지도를 고를 수 있습니다. "돋보기"를 체크하면 마우스를 올린 곳을 확대해 볼 수 있습니다(기본은 꺼짐). 차이 지도는 일반 확대와 AI 결과의 차이를 픽셀마다 계산해 많이 달라진 곳을 밝게 보여 줍니다.
업로드는 jpg·png 10MB 이하, 긴 변 2048px 이하이고 2048px 컬러 사진이 약 43초 걸렸습니다(CPU). 복원하기 전에 잡음 강도(0~1)와 얼굴 복원 사용 여부, 흑백일 때 컬러화 강도를 고를 수 있고, 결과 아래에 모델·잡음·배율·출력 크기·처리 시간이 한 줄로 나옵니다. 결과는 "다운로드" 버튼으로 받습니다.
흑백 입력은 컬러화 토글을 켜서 AI 추정 색을 볼 수 있습니다. 색은 사진에 있던 정보가 아니라 AI가 추정한 값이라서, 복원 결과와 나란히 두고 따로 보여 줍니다.
검증 결과
사전에 정한 합격선에 못 미쳤습니다. 보류셋 유효 14장(얼굴 표시 1장 제외) 기준이며, 판정자는 AI 두 모델입니다.
항목 | 결과 | 기준 |
|---|---|---|
두 판정자 판정 일치 | 8/14 | 11 이상 |
AI 결과가 일반 확대보다 낫다는 판정(두 판정자 모두) | 5/14 | 11 이상 |
결함이 표시된 이미지 | 8/14 | 2 이하 |
개발셋(15장) 반복에서도 최선이 결과 우세 11/15, 결함 4였고 목표(12 이상, 3 이하)에 닿지 못했습니다. 설정을 바꾼 네 번의 결과는 다음과 같습니다.
회차 | 선택한 설정 | 결과 우세 | 결함 |
|---|---|---|---|
1 | Real-ESRGAN x4plus, 확대만(2배) | 8 | 3 |
2 | x4plus, tile 없음 | 10 | 4 |
3 | realesr-general-x4v3, 잡음 강도 0.5 | 11 | 4 |
4 | 같은 모델, 잡음 강도 0.25 | 11 | 4 |
같은 설정도 판정 때마다 1~2장씩 달라져서 11과 12의 차이는 개선인지 잡음인지 구분되지 않았습니다. 판정자가 AI이고 표본이 작아서 이 숫자는 성능 근거가 아니라 한계를 보여 주는 기록입니다.
담당자가 샘플을 직접 볼 때는 일반 확대보다 낫게 복원됐다고 판단했지만, 샘플은 결과가 좋은 이미지를 골라 둔 것이라 정량 근거는 아닙니다.
문제 해결과 한계
- 가장 큰 원인은 AI가 옛 인쇄물의 망점을 잡음으로 보고 매끈하게 지워 버리는 것이었습니다. 모델을 하나 더 도입해 봤지만 합격선에는 닿지 않았습니다. 합격선은 중간에 낮추지 않고 결과를 그대로 기록하기로 했습니다.
- 샘플 8장은 결과가 좋았던 이미지를 골라 보여 주는 선별 사례이고 성능을 대표하지 않습니다. 영상도 1건·5초라 일반화할 수 없습니다.
- 모델 가중치와 영상 소재의 라이선스는 배포 전에 검토가 필요합니다.
개발 경험
- 좋았던 점은 요구사항 → 설계 → 구현 → 리뷰를 역할로 나눠 돌리니 검토에서 실제 결함(업로드 폭탄 이미지, 임시 파일 정리 등)이 걸렸다는 것입니다. 사전 등록 덕분에 결과가 나빠도 기준을 바꾸지 않고 사실대로 남길 수 있었습니다.
- 아쉬운 점은 AI 판정자의 잡음이 개선 폭보다 컸습니다.
- 처음부터 시연이 편리한 웹 개발을 고려할 수 있었지만, 현재는 일단 웹 전환 보류한 상태입니다.