영상 로고/자막 제거 (video-watermark-remover)
ProPainter를 사용하여 비디오에서 고정 위치의 로고, 모서리 마크 및 자막(화면에 고정된)을 제거합니다. 요소 주변의 ROI(관심 영역)만 잘라서 인페인팅한 후, 가장자리를 부드럽게 혼합하여 원본 영상에 다시 붙입니다. ROI 외부의 픽셀은 변경되지 않습니다.
⚠ ProPainter의 라이선스는 S-Lab License 1.0입니다. 비상업적 용도로만 사용 가능하며, 상업적 사용을 위해서는 원저작자에게 별도 문의해야 합니다. 또한, 사용자는 처리 권한이 있는 콘텐츠에만 이 도구를 사용해야 합니다.
0. 환경 설정 (최초 사용)
모든 스크립트는 scripts/ 디렉토리에 있습니다. 작업 디렉토리를 이 스킬 디렉토리(즉, SKILL.md 파일이 있는 곳)로 설정해야 합니다.
python scripts/setup_env.py # ffmpeg / PyTorch(CUDA) 확인, 고정 버전의 ProPainter 클론, config.json 생성
python scripts/selftest.py # 합성 테스트 비디오로 전체 흐름 실행, 정답과 비교하여 PASS/FAIL 출력
config.json이 없으면setup_env.py가 누락된 항목과 설치 방법을 안내합니다. PyTorch는 사용자가 자신의 그래픽 카드에 맞는 CUDA 버전을 설치해야 합니다.- 자체 테스트가 통과하지 않으면 계속 진행하지 말고, 먼저 안내에 따라 환경을 수정하세요.
- 아래의
python은setup_env.py가config.json에 기록한 인터프리터(즉,setup_env.py를 실행할 때 사용한 환경)를 가리킵니다.
사용자가 제공해야 할 정보
| 항목 | 필수 | 설명 |
|---|---|---|
| 비디오 파일 | ✅ | 하나 이상의 비디오 파일 |
| 제거할 대상 | ✅ | 일상적인 표현으로 충분: "왼쪽 위 로고", "중앙 큰 로고", "하단 자막" |
| 위치 힌트 | 선택 | 동일한 유형의 요소가 여러 개 있을 때만 필요 |
| 시간 구간 | 선택 | 기본값: 자동 감지. 일부 구간을 유지하려는 경우에만 필요 |
| 후속 작업 계획 | 권장 질문 | 새 로고 오버레이 여부, 자막 재삽입 여부, 새 자막에 배경 막대가 있는지 여부. 배경 막대가 있으면 인페인팅 대신 흐림 처리만으로 충분 |
필수 항목이 없으면 사용자에게 질문하고, 선택 항목은 묻지 마세요.
기능 범위 (작업 시작 전 확인, 범위를 벗어나는 경우 먼저 사용자에게 알림)
- ✅ 고정 위치의 로고, 모서리 마크, 워터마크 (반투명, 페이드 인/아웃, 오프닝/엔딩 전환 장면에서 흐려진 경우 포함).
- ✅ 고정 위치, 수평 중앙, 단일 행의 자막 (화면에 고정된), 흰색 또는 어두운 글자, 그림자 유무 관계없음.
- ⚠️ 움직이거나 크기가 변하는 로고, 스크롤 자막, 여러 행 자막, 중앙에 없는 자막: 스크립트가 자동으로 추적할 수 없으며, 사용자가 시간 구간을 지정한 후
--block,--grow-from옵션으로 수동 처리해야 하며, 결과물 품질이 저하될 수 있습니다. - ⚠️ 화면에 원래 있는 텍스트(간판, 화면 내 글자)가 제거 대상 요소와 겹치는 경우 함께 흐려질 수 있습니다. 식별 목록에서 사용자에게 이 사실을 알려야 합니다.
- NVIDIA GPU 필요, 가용 VRAM 4GB 이상 권장. CPU만으로도 실행 가능하지만 매우 느립니다.
프로세스 (8단계, 순차 진행)
중간 파일은 임시 작업 디렉토리 $W에 저장됩니다. 최종 결과물은 원본 비디오 옆에, 품질 검사 이미지는 <비디오 디렉토리>/qc/에 저장됩니다.
1. 탐지
python scripts/probe.py IN1.mp4 [IN2.mp4] --out-dir $W
샘플 프레임 모자이크를 보고 다음을 판단합니다:
- 요소 위치
- 모서리 마크인지 중앙 큰 로고인지
- 페이드 인 (로고가 안정적으로 나타나는 프레임 구간 기록), 블랙 장면, 확대/축소 또는 흐림 전환 장면 유무
- 자막 유무
2. 위치 파악 (정적 로고)
python scripts/locate_static.py --video IN1.mp4@시작프레임:종료프레임 [--video IN2.mp4@a:b] --out-dir $W
- 시작/종료 프레임은 페이드 인, 블랙 장면, 전환 장면을 피해야 합니다.
- 여러 비디오에 동일한 로고가 있을 경우 함께 전달합니다. 스크립트는 "모든 비디오에서 정지된" 픽셀만 선택하여 배경에서 우연히 정지된 블록을 배제합니다.
static_rel.png(어두울수록 정지된 영역)을 보고, 이를 기준으로 로고의 대략적인 박스--box와 임계값 결정: 단색 로고 0.3–0.5, 반투명 로고 0.6–0.75.
3. 마스크 생성 및 반드시 사용자 확인
정적 로고:
python scripts/mask_logo.py --rel $W/static_rel.npy --box x0,y0,x1,y1 --thresh 0.5 \
[--block 사각형] [--clear 영역 --block 영역] --preview IN1.mp4@프레임 [--preview IN2.mp4@프레임] --out-dir $W
- 가느다란 요소(텍스트, 선, 줄)는 획 단위로 마스킹합니다. 획 사이의 실제 배경은 모델이 참조할 수 있도록 남겨둡니다.
--block사각형은 두 가지 경우에만 사용합니다: 단색 큰 블록이거나, 너무 가늘어 완전히 마스킹하기 어려운 작은 글자(예: 작은 영문 한 줄).- ❌ 가느다란 요소를 통째로 덮지 마십시오. 카메라가 천천히 확대될 때, 덮인 부분에서 실제 배경이 거의 노출되지 않아 모델이 픽셀을凭空 생성해야 하며, 이로 인해 "비늘 모양 텍스처"가 나타날 수 있습니다.
- 미리보기 이미지에서 요소의 가장자리와 꼬리 부분이 누락되었는지 확인하십시오. 누락된 부분은 인페인팅 시 내부로 끌려 들어와 컬러 라인을 남깁니다.
자막:
python scripts/mask_subtitle.py IN.mp4 --out-dir $W [--rows y0,y1] [--dark] [--shadow 0,0]
--rows를 지정하지 않으면 화면 하단 40% 범위에서 자동으로 자막 줄을 찾습니다. 잘못 찾은 경우 수동으로 지정하십시오.- 출력에서 "화면 가장자리에 가까움" 경고를 주의하십시오.
사용자 확인: mask_preview.png 또는 subtitle_mask_preview.png를 사용자에게 보여주고, 식별 목록도 함께 제공하십시오. 좌표는 mask_logo.py가 출력한 값을 사용합니다. 예:
① 왼쪽 상단 로고: 인페인팅 박스 (21,15)–(124,50), 전체 구간 등장 ② 하단 자막: y485–524, 총 47개 문장 ③ 1:34 지점의 화면 간판이 인페인팅 영역 내에 있어 함께 흐려질 수 있음
사용자 확인 후에만 5단계로 진행합니다.
4. 구간 분할
python scripts/shots.py IN.mp4 --out $W/bounds.json [--extra 178]
하드 컷과 페이드 인/아웃 전환 장면이 모두 분할됩니다. 구간 분할 없이 실행하면, 정적인 장면에서 인접 장면 색상의 "알약 모양 색 블록"이 나타날 수 있습니다.
5+6. 인페인팅 + 합성
긴 비디오는 백그라운드에서 실행하고, 주기적으로 로그를 확인하며 사용자에게 진행 상황을 능동적으로 알리십시오.
python scripts/inpaint.py --video IN.mp4 (--mask $W/mask.npy | --masks $W/masks.npz) --bounds $W/bounds.json \
--workdir $W/run --out OUT.mp4 [--mask-dilation 2] [--grow-from 178:14] [--workers 2]
--mask-dilation: 기본 2; 반투명 또는 글로우가 있는 로고는 4를 사용하십시오.--grow-from 프레임:픽셀: 특정 프레임부터 마스크를 추가로 확장하여, 확대/축소 또는 흐림 전환 장면에서 흐트러진 로고를 덮습니다.- 동일한 비디오에서 로고와 자막을 모두 제거해야 하는 경우, 두 번 나누어 실행하십시오. 두 번째 실행의
--video는 첫 번째 출력을 사용하고, 두 ROI를 하나의 큰 박스로 합치지 마십시오. - 중단 시 동일한 명령어로 다시 실행하면, 이미 완료된 구간은 건너뜁니다.
- 출력 파일 이름은
원래이름-nologo.mp4또는원래이름-nologo-nosub.mp4형식입니다. 스크립트는 원본 파일을 덮어쓰지 않습니다. - 소요 시간 참고 (RTX 3060, 2병렬): 960×540 해상도 126초 비디오, 왼쪽 상단 로그 약 17분, 자막 포함 약 21분; 5초짜리 중앙 큰 로고 약 1분.
7. 품질 검사 (QC)
python scripts/qc.py --orig IN.mp4 --out OUT.mp4 (--mask .. | --masks ..) --bounds $W/bounds.json --workdir $W/run \
[--residue blue] [--times 3,12,20] --out-dir <비디오 디렉토리>/qc/xxx
수치 지표:
- 잔상: 결과 프레임의 분산이 외부 영역 분산과 거의 같아야 함.
- 깜빡임: 중앙값 비율이 1 또는 그 이하여야 하며, 급격한 점프 프레임이 거의 없어야 함.
- 이음새: 비율이 약 1이어야 함.
- 외부 오류: 2 미만.
- 잔류 색상: 0에 가깝게 감소해야 함.
- 완전 검은 프레임: 0프레임.
⚠ 모든 수치 지표가 통과하더라도 국부적인 문제가 있을 수 있으므로, 반드시 sheet_XX.png를 한 페이지씩 열어 육안으로 검사하십시오 (색 블록, 비늘 텍스처, 잔여 글자, 컬러 라인). 문제 발견 시 해당 단계로 돌아가 수정하십시오:
- 색 블록:
shots.py --extra로 컷 포인트 추가. - 비늘 텍스처: 획 단위 마스킹으로 변경.
- 잔여 라인:
--block옵션 추가.
수정 후에는 영향을 받은 부분만 다시 실행하고, 다시 QC를 수행하십시오.
8. 전달
- 결과 비디오와 비교 이미지의 절대 경로를 제공하고,
before_after.png를 사용자에게 보여주십시오. - 여러 비디오를 연결해야 하는 경우
python scripts/concat.py A.mp4 B.mp4 C.mp4 --out FULL.mp4명령어를 사용하십시오. 스크립트가 총 프레임 수가 각 부분의 합과 일치하는지 검증합니다. - 좌표 문서를 작성하십시오. 템플릿은
references/positions-template.md를 참조하십시오. 각 요소에 대해 세 가지 레이어 박스를 작성합니다: 원래 요소 범위, 실제 인페인팅 범위, 블렌딩된 가장 바깥쪽 범위 (새 로고가 덮을 권장 영역). 자막의 경우subtitle_info.json의union_box를 사용하십시오. - 알려진 결함은 시간별로 명확히 나열하고 숨기지 마십시오.
하드 규칙 (모두 실제 테스트에서 얻은 교훈)
- ROI의 너비와 높이는 모두 ≥160이어야 합니다. 높이가 96이면 ProPainter가 마스크 영역에서 완전 검은색을 출력하며, fp32로 변경해도 동일합니다.
inpaint.py는 ROI를 선택할 때 이미 이 조건을 보장합니다. - 전환 장면은 반드시 분할해야 합니다. 하드 컷만 분할하면 페이드 인/아웃이 누락되어 ProPainter가 다른 장면의 픽셀을 차용하여 색 블록이 나타납니다. 마스크 확장이나 fp32 사용으로는 해결할 수 없습니다.
- 가느다란 요소는 획 단위로 마스킹하고, 통째로 덮지 마십시오.
- 자막은 중앙 대칭으로 잘라야 하며, 동일한 문장에는 동일한 마스크를 사용해야 합니다. 대칭 자르기를 하지 않으면 배경의 밝은 줄이 마스크에 포함되고, 동일한 문장에 다른 마스크를 사용하면 깜빡임이 발생하며, 페이드 인 시에도 누락이 발생합니다.
- 수치 지표만으로는 문제를 놓칠 수 있으므로, 반드시 한 페이지씩 육안으로 검사하십시오.
- 인페인팅 전에 반드시 사용자에게 마스크를 확인받아야 합니다. 원본 파일을 덮어쓰거나 사용자의 파일을 삭제하지 마십시오.
- 백그라운드 서브 프로세스는 창을 띄우지 않습니다 (스크립트에 내장됨). 긴 작업은 백그라운드에서 실행하고, 진행 상황을 적극적으로 보고하십시오.