Ver3.5 영상 전처리와 후처리 파이프라인 구축 완전 정복 🎥✨

영상 전처리와 후처리 파이프라인 구축 완전 정복 🎥✨
RAW 영상부터 완성본까지, 데이터 흐름을 설계하는 프로의 방식
야 솔직히 말해서 영상 작업하다 보면 이런 경험 다들 있지 않음? 🤔
촬영 끝나고 편집 들어가려는데 파일 포맷이 제각각이고, 해상도도 다르고, 색감도 들쑥날쑥하고...
그냥 멘탈 나가는 거임 ㅋㅋㅋ 근데 이게 다 파이프라인이 없어서 생기는 문제야.
오늘은 영상 전처리(Pre-processing)부터 후처리(Post-processing)까지 체계적인 파이프라인을 어떻게 구축하는지 진짜 실무 기준으로 파헤쳐볼 거야.
개발자든, 영상 편집자든, 유튜버든 이 개념 알면 작업 효율이 진짜 레벨업 됨 🚀
파이프라인이 뭔지부터 제대로 알고 가자
파이프라인(Pipeline)이라는 단어, 원래 석유 파이프에서 나온 말이야.
석유가 한 방향으로 쭉 흘러가듯이, 데이터(영상)가 여러 처리 단계를 순서대로 통과하는 구조를 파이프라인이라고 해.
영상 파이프라인은 크게 두 파트로 나뉘어:
포맷 변환, 해상도 통일, 노이즈 제거, 색공간 변환 등이 여기 해당해.
색보정, 자막 삽입, 워터마크, 오디오 믹싱, 최종 인코딩 최적화 등이 여기야.
근데 왜 이걸 굳이 파이프라인으로 구축해야 하냐고? 🤔
그냥 하나씩 수동으로 하면 안 되냐고?
안 돼. 진짜로. 이유를 보자:
전처리(Pre-processing) 단계 완전 분해
전처리는 말 그대로 본 작업 전에 데이터를 깔끔하게 만드는 과정이야.
이 단계를 제대로 안 하면 뒤에서 아무리 잘해도 결과물이 이상하게 나와. 기초공사가 중요한 거랑 같은 원리임.
① 포맷 변환 (Format Conversion)
현장에서 촬영된 영상은 카메라마다 포맷이 달라.
소니는 XAVC, 캐논은 C-Log, DJI 드론은 D-Log, 스마트폰은 HEVC(H.265)...
이걸 통일된 포맷으로 변환하는 게 첫 번째야.
MKV — 오픈소스, 다중 오디오/자막 트랙 지원, 편집용으로 좋음
MOV — 애플 생태계 최적화, ProRes 코덱 지원
AVI — 구형 포맷, 호환성은 좋지만 파일 크기 큼
WebM — 웹 스트리밍 특화, VP8/VP9/AV1 코덱
FFmpeg으로 포맷 변환하는 기본 명령어:
# MP4로 변환 (H.264 코덱, AAC 오디오)
ffmpeg -i input.mov -c:v libx264 -c:a aac -crf 23 output.mp4
# H.265(HEVC)로 변환 (더 높은 압축률)
ffmpeg -i input.mp4 -c:v libx265 -c:a aac -crf 28 output_hevc.mp4
# WebM으로 변환 (VP9 코덱)
ffmpeg -i input.mp4 -c:v libvpx-vp9 -c:a libopus output.webm
② 해상도 정규화 (Resolution Normalization)
여러 소스에서 온 영상들은 해상도가 제각각이야.
4K(3840×2160), FHD(1920×1080), HD(1280×720), 세로형(1080×1920)...
이걸 파이프라인에서 처리하려면 기준 해상도로 통일하거나, 메타데이터로 분류해야 해.
# 1920x1080으로 리사이즈 (비율 유지, 패딩 추가)
ffmpeg -i input.mp4 -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2" output.mp4
# Python + OpenCV로 해상도 확인 및 리사이즈
import cv2
cap = cv2.VideoCapture('input.mp4')
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
print(f"원본 해상도: {width}x{height}")
③ 색공간 변환 (Color Space Conversion)
이게 진짜 중요한데 많이들 놓치는 부분이야 ㅠㅠ
영상의 색공간(Color Space)이 맞지 않으면 색이 이상하게 보이거나, AI 모델 입력으로 쓸 때 결과가 틀려.
YUV / YCbCr — 영상 압축에 최적화, 인간 시각 특성 반영
HSV / HSL — 색상·채도·명도 분리, 색 기반 처리에 유리
LAB — 인간 지각과 가장 유사, 색보정 작업에 많이 씀
BT.709 / BT.2020 — 방송 표준 색역, HDR 콘텐츠는 BT.2020
# OpenCV에서 색공간 변환
import cv2
frame = cv2.imread('frame.jpg')
# BGR → RGB (OpenCV는 기본이 BGR임!)
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# BGR → YUV
yuv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2YUV)
# BGR → HSV (색 기반 마스킹에 유용)
hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
④ 노이즈 제거 (Noise Reduction)
저조도 환경에서 촬영된 영상이나 오래된 영상에는 노이즈가 많아.
이걸 전처리 단계에서 제거해줘야 이후 처리 품질이 올라가.
# FFmpeg 내장 노이즈 필터
ffmpeg -i noisy.mp4 -vf "hqdn3d=4:3:6:4.5" denoised.mp4
# OpenCV 노이즈 제거
import cv2
import numpy as np
frame = cv2.imread('noisy_frame.jpg')
# Non-local Means Denoising (품질 좋지만 느림)
denoised = cv2.fastNlMeansDenoisingColored(frame, None, 10, 10, 7, 21)
# 가우시안 블러 (빠르지만 디테일 손실)
blurred = cv2.GaussianBlur(frame, (5, 5), 0)
⑤ 프레임레이트 정규화 (FPS Normalization)
24fps, 25fps, 30fps, 60fps, 120fps... 소스마다 다 달라.
파이프라인에서는 이것도 통일해줘야 해.
# 30fps로 변환
ffmpeg -i input.mp4 -r 30 output_30fps.mp4
# 프레임 보간으로 24fps → 60fps (품질 좋은 방법)
ffmpeg -i input.mp4 -vf "minterpolate=fps=60:mi_mode=mci" output_60fps.mp4
✅ 해상도 정규화 완료?
✅ 색공간 확인 완료?
✅ 노이즈 수준 체크 완료?
✅ FPS 통일 완료?
✅ 오디오 샘플레이트 통일 완료? (보통 44100Hz 또는 48000Hz)
핵심 처리 단계: 트랜스코딩과 영상 변환
전처리가 끝나면 이제 진짜 본 작업이야.
영상 파이프라인에서 핵심 처리는 주로 트랜스코딩(Transcoding)과 영상 분석/변환이야.
트랜스코딩 (Transcoding)
트랜스코딩은 한 코덱에서 다른 코덱으로 변환하는 거야.
단순히 포맷만 바꾸는 게 아니라 압축 방식 자체를 바꾸는 것이라 품질과 파일 크기에 직접 영향을 줘.
| 코덱 | 압축률 | 품질 | 주요 용도 |
|---|---|---|---|
| H.264 (AVC) | 보통 | 좋음 | 웹 스트리밍, 일반 배포 |
| H.265 (HEVC) | 높음 | 매우 좋음 | 4K, 스트리밍 서비스 |
| AV1 | 매우 높음 | 최고 | 유튜브, 넷플릭스 차세대 |
| VP9 | 높음 | 좋음 | WebM, 구글 서비스 |
| ProRes | 낮음 | 최고 (편집용) | 영상 편집 중간 파일 |
| DNxHD/DNxHR | 낮음 | 최고 (편집용) | Avid 편집 환경 |
보통 18~28 사이를 쓰고, 23이 기본값이야.
낮을수록 품질 좋고 파일 크기 커짐. 유튜브 업로드용은 18~20 추천!
어댑티브 비트레이트 스트리밍 (ABR)
유튜브나 넷플릭스에서 인터넷 속도에 따라 화질이 자동으로 바뀌는 거 알지?
그게 바로 HLS(HTTP Live Streaming)나 DASH(Dynamic Adaptive Streaming over HTTP)야.
파이프라인에서 이걸 구현하려면 하나의 영상을 여러 해상도/비트레이트로 동시에 인코딩해야 해.
# FFmpeg으로 HLS 멀티 비트레이트 스트림 생성
ffmpeg -i input.mp4 \
-map 0:v -map 0:a -map 0:v -map 0:a -map 0:v -map 0:a \
-c:v libx264 -c:a aac \
-b:v:0 800k -s:v:0 640x360 \
-b:v:1 1400k -s:v:1 842x480 \
-b:v:2 2800k -s:v:2 1280x720 \
-var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" \
-master_pl_name master.m3u8 \
-f hls -hls_time 6 -hls_list_size 0 \
stream_%v/index.m3u8
GPU 가속 인코딩
대량의 영상을 처리할 때 CPU만 쓰면 너무 느려.
NVIDIA GPU가 있다면 NVENC를, AMD GPU라면 AMF를, 인텔이라면 QSV를 쓸 수 있어.
# NVIDIA GPU 가속 인코딩 (NVENC)
ffmpeg -i input.mp4 -c:v h264_nvenc -preset p6 -cq 23 output.mp4
# AMD GPU 가속 (AMF)
ffmpeg -i input.mp4 -c:v h264_amf -quality quality output.mp4
# Intel QSV 가속
ffmpeg -i input.mp4 -c:v h264_qsv -global_quality 23 output.mp4
NVIDIA RTX 3080 (NVENC): 약 200~400fps 처리
→ GPU 가속이 약 5~10배 빠름! 대량 처리 시 필수야.
후처리(Post-processing) 단계 완전 분해
후처리는 영상을 완성본으로 다듬는 마지막 단계야.
여기서 색보정, 자막, 오디오 처리, 워터마크, 최종 최적화가 이루어져.
이 단계가 결과물의 퀄리티를 결정하는 거라 진짜 중요함 ㅋㅋㅋ
① 색보정 (Color Grading & Correction)
색보정은 두 가지로 나뉘어:
Color Correction — 색을 정확하게 맞추는 것 (화이트밸런스, 노출 보정)
Color Grading — 의도적으로 분위기를 만드는 것 (영화 느낌, 빈티지 등)
# FFmpeg으로 기본 색보정
# 밝기, 대비, 채도 조정
ffmpeg -i input.mp4 -vf "eq=brightness=0.05:contrast=1.1:saturation=1.2" output.mp4
# 색온도 조정 (화이트밸런스)
ffmpeg -i input.mp4 -vf "colorbalance=rs=0.1:gs=0:bs=-0.1" output.mp4
# LUT(Look-Up Table) 적용 (영화 느낌 색보정)
ffmpeg -i input.mp4 -vf "lut3d=file=cinematic.cube" output.mp4
영화 촬영 현장에서 많이 쓰는 .cube 파일 형식이 표준이야.
무료 LUT는 Lutify.me, RocketStock 등에서 구할 수 있어.
② 자막 삽입 (Subtitle Embedding)
자막은 두 가지 방식으로 삽입할 수 있어:
하드 서브(Hard Sub) — 영상에 직접 구워 넣는 방식, 항상 보임
소프트 서브(Soft Sub) — 별도 트랙으로 저장, 켜고 끌 수 있음
# 하드 서브 (SRT 파일을 영상에 직접 삽입)
ffmpeg -i input.mp4 -vf "subtitles=subtitle.srt" output_hardsub.mp4
# 소프트 서브 (MKV 컨테이너에 자막 트랙 추가)
ffmpeg -i input.mp4 -i subtitle.srt -c copy -c:s srt output_softsub.mkv
# ASS 형식 자막 (스타일 지정 가능)
ffmpeg -i input.mp4 -vf "ass=subtitle.ass" output.mp4
③ 워터마크 삽입 (Watermark)
# 이미지 워터마크 삽입 (우측 하단, 반투명)
ffmpeg -i input.mp4 -i watermark.png \
-filter_complex "overlay=W-w-10:H-h-10:alpha=0.7" \
output_watermark.mp4
# 텍스트 워터마크
ffmpeg -i input.mp4 \
-vf "drawtext=text='© 재능넷':fontsize=24:fontcolor=white@0.5:x=W-tw-10:y=H-th-10" \
output_text_watermark.mp4
④ 오디오 후처리 (Audio Post-processing)
영상의 오디오도 후처리가 필요해.
노이즈 제거, 볼륨 정규화, EQ, 컴프레서 등을 적용해야 해.
# 오디오 볼륨 정규화 (LUFS 기준, 방송 표준 -23 LUFS)
ffmpeg -i input.mp4 -af "loudnorm=I=-23:TP=-1.5:LRA=11" output.mp4
# 배경 노이즈 제거 (afftdn 필터)
ffmpeg -i input.mp4 -af "afftdn=nf=-25" output_denoised.mp4
# 오디오 EQ (저음 강화)
ffmpeg -i input.mp4 -af "equalizer=f=100:width_type=o:width=2:g=3" output.mp4
# 스테레오 → 모노 변환
ffmpeg -i input.mp4 -ac 1 output_mono.mp4
⑤ 썸네일 자동 생성
파이프라인에서 썸네일도 자동으로 생성할 수 있어.
유튜브 업로드 자동화할 때 특히 유용함.
# 특정 시간대 썸네일 추출
ffmpeg -i input.mp4 -ss 00:00:05 -vframes 1 thumbnail.jpg
# 영상에서 가장 대표적인 프레임 자동 선택 (썸네일 최적화)
ffmpeg -i input.mp4 -vf "thumbnail=300" -frames:v 1 best_thumbnail.jpg
# 여러 썸네일 추출 (10초마다)
ffmpeg -i input.mp4 -vf "fps=1/10" thumbnail_%03d.jpg
실전 파이프라인 구축: Python으로 자동화하기
이제 진짜 실전이야! 🔥
Python으로 전처리부터 후처리까지 자동화된 파이프라인을 구축해보자.
MoviePy와 FFmpeg-python을 활용할 거야.
pip install moviepy ffmpeg-python opencv-python numpy
기본 파이프라인 클래스 구조
import ffmpeg
import cv2
import os
from pathlib import Path
class VideoPipeline:
"""영상 전처리 & 후처리 파이프라인"""
def __init__(self, input_path: str, output_dir: str):
self.input_path = input_path
self.output_dir = Path(output_dir)
self.output_dir.mkdir(exist_ok=True)
self.metadata = self._extract_metadata()
def _extract_metadata(self) -> dict:
"""영상 메타데이터 추출"""
probe = ffmpeg.probe(self.input_path)
video_stream = next(
(s for s in probe['streams'] if s['codec_type'] == 'video'), None
)
return {
'width': int(video_stream['width']),
'height': int(video_stream['height']),
'fps': eval(video_stream['r_frame_rate']),
'duration': float(probe['format']['duration']),
'codec': video_stream['codec_name']
}
def preprocess(self, target_resolution=(1920, 1080), target_fps=30):
"""전처리: 해상도, FPS 정규화"""
preprocessed_path = self.output_dir / 'preprocessed.mp4'
stream = ffmpeg.input(self.input_path)
stream = ffmpeg.filter(stream, 'scale',
target_resolution[0], target_resolution[1],
force_original_aspect_ratio='decrease')
stream = ffmpeg.filter(stream, 'pad',
target_resolution[0], target_resolution[1],
'(ow-iw)/2', '(oh-ih)/2')
stream = ffmpeg.filter(stream, 'fps', fps=target_fps)
ffmpeg.output(stream, str(preprocessed_path),
vcodec='libx264', crf=18,
acodec='aac', ar=48000).run(overwrite_output=True)
self.preprocessed_path = str(preprocessed_path)
print(f"✅ 전처리 완료: {preprocessed_path}")
return self
def denoise(self):
"""노이즈 제거"""
denoised_path = self.output_dir / 'denoised.mp4'
stream = ffmpeg.input(self.preprocessed_path)
stream = ffmpeg.filter(stream, 'hqdn3d', 4, 3, 6, 4.5)
ffmpeg.output(stream, str(denoised_path),
vcodec='libx264', crf=18).run(overwrite_output=True)
self.preprocessed_path = str(denoised_path)
print(f"✅ 노이즈 제거 완료")
return self
def color_grade(self, lut_path=None):
"""색보정 (LUT 적용 또는 기본 보정)"""
graded_path = self.output_dir / 'color_graded.mp4'
stream = ffmpeg.input(self.preprocessed_path)
if lut_path:
stream = ffmpeg.filter(stream, 'lut3d', file=lut_path)
else:
stream = ffmpeg.filter(stream, 'eq',
brightness=0.02, contrast=1.05, saturation=1.1)
ffmpeg.output(stream, str(graded_path),
vcodec='libx264', crf=18).run(overwrite_output=True)
self.processed_path = str(graded_path)
print(f"✅ 색보정 완료")
return self
def add_watermark(self, watermark_text="© 재능넷"):
"""워터마크 삽입"""
watermarked_path = self.output_dir / 'watermarked.mp4'
stream = ffmpeg.input(self.processed_path)
stream = ffmpeg.drawtext(stream,
text=watermark_text,
fontsize=20,
fontcolor='white@0.6',
x='W-tw-10', y='H-th-10')
ffmpeg.output(stream, str(watermarked_path),
vcodec='libx264', crf=18).run(overwrite_output=True)
self.processed_path = str(watermarked_path)
print(f"✅ 워터마크 삽입 완료")
return self
def normalize_audio(self):
"""오디오 볼륨 정규화 (-23 LUFS)"""
normalized_path = self.output_dir / 'audio_normalized.mp4'
stream = ffmpeg.input(self.processed_path)
audio = ffmpeg.filter(stream.audio, 'loudnorm',
I=-23, TP=-1.5, LRA=11)
ffmpeg.output(stream.video, audio, str(normalized_path),
vcodec='copy').run(overwrite_output=True)
self.processed_path = str(normalized_path)
print(f"✅ 오디오 정규화 완료")
return self
def export(self, output_name='final_output.mp4', crf=23):
"""최종 출력"""
final_path = self.output_dir / output_name
stream = ffmpeg.input(self.processed_path)
ffmpeg.output(stream, str(final_path),
vcodec='libx264', crf=crf,
acodec='aac', ar=48000,
movflags='faststart').run(overwrite_output=True)
print(f"🎉 최종 출력 완료: {final_path}")
return str(final_path)
# 파이프라인 실행 예시
pipeline = VideoPipeline('input_video.mp4', './output')
result = (pipeline
.preprocess(target_resolution=(1920, 1080), target_fps=30)
.denoise()
.color_grade()
.add_watermark("© 내 채널")
.normalize_audio()
.export('final.mp4', crf=20)
)
return self를 각 메서드에서 반환하면 .preprocess().denoise().color_grade() 처럼 연결해서 쓸 수 있어. 파이프라인 코드가 훨씬 읽기 쉬워짐!
클라우드 기반 영상 파이프라인 서비스
로컬에서 직접 구축하는 것도 좋지만, 대규모 처리가 필요하다면 클라우드 서비스를 활용하는 게 훨씬 효율적이야.
주요 클라우드 영상 처리 서비스들을 알아보자.
HLS, DASH, MP4 등 다양한 출력 형식 지원. 분당 과금 방식.
영상 전처리 단계에서 콘텐츠 분류 자동화에 활용 가능.
Content Protection(DRM) 기능도 내장되어 있어.
특히 Mux는 개발자 친화적인 API로 유명해.
AWS Lambda + S3 자동화 파이프라인 구조
# AWS Lambda 함수 예시 (S3 업로드 시 자동 트랜스코딩)
import boto3
import json
def lambda_handler(event, context):
mediaconvert = boto3.client('mediaconvert',
endpoint_url='https://your-endpoint.mediaconvert.region.amazonaws.com')
# S3 이벤트에서 파일 정보 추출
bucket = event['Records'][0]['s3']['bucket']['name']
key = event['Records'][0]['s3']['object']['key']
input_url = f"s3://{bucket}/{key}"
output_url = f"s3://output-bucket/processed/"
job_settings = {
"Inputs": [{
"FileInput": input_url,
"AudioSelectors": {"Audio Selector 1": {"DefaultSelection": "DEFAULT"}},
"VideoSelector": {}
}],
"OutputGroups": [{
"Name": "File Group",
"OutputGroupSettings": {
"Type": "FILE_GROUP_SETTINGS",
"FileGroupSettings": {"Destination": output_url}
},
"Outputs": [{
"VideoDescription": {
"CodecSettings": {
"Codec": "H_264",
"H264Settings": {"RateControlMode": "QVBR", "QvbrSettings": {"QvbrQualityLevel": 7}}
},
"Width": 1920, "Height": 1080
},
"AudioDescriptions": [{
"CodecSettings": {"Codec": "AAC", "AacSettings": {"Bitrate": 128000, "SampleRate": 48000}}
}],
"ContainerSettings": {"Container": "MP4"}
}]
}]
}
response = mediaconvert.create_job(
Role='arn:aws:iam::account:role/MediaConvertRole',
Settings=job_settings
)
return {'statusCode': 200, 'body': json.dumps('트랜스코딩 작업 시작!')}
AI 기반 영상 처리: 파이프라인의 미래
요즘 파이프라인에 AI가 들어오면서 진짜 게임체인저가 됐어.
예전엔 사람이 직접 해야 했던 작업들이 자동화되고 있거든 ㅋㅋㅋ
PySceneDetect 라이브러리로 쉽게 구현 가능.
from scenedetect import VideoManager, SceneManager
from scenedetect.detectors import ContentDetector
def detect_scenes(video_path):
video_manager = VideoManager([video_path])
scene_manager = SceneManager()
scene_manager.add_detector(ContentDetector(threshold=30.0))
video_manager.start()
scene_manager.detect_scenes(frame_source=video_manager)
scene_list = scene_manager.get_scene_list()
print(f"총 {len(scene_list)}개 장면 감지됨")
for i, scene in enumerate(scene_list):
print(f"장면 {i+1}: {scene[0].get_timecode()} ~ {scene[1].get_timecode()}")
return scene_list
MediaPipe, YOLO, DeepSORT 등을 파이프라인에 통합 가능.
한국어 포함 99개 언어 지원, 정확도도 엄청 높아.
import whisper
def auto_subtitle(video_path, output_srt_path):
"""Whisper로 자동 자막 생성"""
model = whisper.load_model("large") # 모델 크기: tiny/base/small/medium/large
result = model.transcribe(video_path, language='ko')
# SRT 파일 생성
with open(output_srt_path, 'w', encoding='utf-8') as f:
for i, segment in enumerate(result['segments']):
start = segment['start']
end = segment['end']
text = segment['text'].strip()
# SRT 형식으로 변환
f.write(f"{i+1}\n")
f.write(f"{format_time(start)} --> {format_time(end)}\n")
f.write(f"{text}\n\n")
print(f"✅ 자막 파일 생성 완료: {output_srt_path}")
def format_time(seconds):
h = int(seconds // 3600)
m = int((seconds % 3600) // 60)
s = int(seconds % 60)
ms = int((seconds % 1) * 1000)
return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}"
Real-ESRGAN, SRCNN, EDVR 등의 모델이 파이프라인에 통합 가능.
720p → 4K 변환도 가능한 시대가 됐어!
파이프라인 성능 최적화 전략
파이프라인 구축했다고 끝이 아니야.
빠르고 효율적으로 돌아가야 진짜 파이프라인이지.
성능 최적화 핵심 전략들을 알아보자.
① 병렬 처리 (Parallel Processing)
여러 영상을 동시에 처리하면 처리 시간이 확 줄어.
Python의 multiprocessing이나 concurrent.futures를 활용해.
from concurrent.futures import ProcessPoolExecutor, as_completed
import os
def process_single_video(video_path):
"""단일 영상 처리 함수"""
pipeline = VideoPipeline(video_path, './output')
return pipeline.preprocess().color_grade().export()
def batch_process(video_list, max_workers=4):
"""배치 처리: 여러 영상 병렬 처리"""
results = []
with ProcessPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(process_single_video, v): v for v in video_list}
for future in as_completed(futures):
video = futures[future]
try:
result = future.result()
results.append(result)
print(f"✅ 완료: {video}")
except Exception as e:
print(f"❌ 실패: {video} - {e}")
return results
# 사용 예시
video_files = ['video1.mp4', 'video2.mp4', 'video3.mp4', 'video4.mp4']
batch_process(video_files, max_workers=os.cpu_count())
② 스트리밍 처리 (Streaming Processing)
대용량 영상을 메모리에 전부 올리지 말고, 청크(chunk) 단위로 처리해야 해.
특히 4K 영상은 프레임 하나가 수십 MB라 메모리 관리가 중요해.
import cv2
def process_video_streaming(input_path, output_path, process_func):
"""스트리밍 방식으로 영상 처리 (메모리 효율적)"""
cap = cv2.VideoCapture(input_path)
fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
frame_count = 0
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# 프레임 단위로 처리
processed_frame = process_func(frame)
out.write(processed_frame)
frame_count += 1
if frame_count % 100 == 0:
print(f"처리 중: {frame_count}프레임")
cap.release()
out.release()
print(f"✅ 총 {frame_count}프레임 처리 완료")
③ 캐싱 전략 (Caching)
같은 전처리를 반복하지 않도록 중간 결과를 캐싱해야 해.
특히 노이즈 제거나 색보정 같은 무거운 작업은 캐시가 필수야.
import hashlib
import os
def get_file_hash(file_path):
"""파일 해시값으로 캐시 키 생성"""
sha256 = hashlib.sha256()
with open(file_path, 'rb') as f:
for chunk in iter(lambda: f.read(8192), b''):
sha256.update(chunk)
return sha256.hexdigest()[:16]
def cached_preprocess(input_path, cache_dir='./cache'):
"""캐시 기반 전처리"""
os.makedirs(cache_dir, exist_ok=True)
file_hash = get_file_hash(input_path)
cache_path = os.path.join(cache_dir, f"{file_hash}_preprocessed.mp4")
if os.path.exists(cache_path):
print(f"✅ 캐시 히트! 전처리 스킵: {cache_path}")
return cache_path
# 캐시 없으면 전처리 실행
print("🔧 전처리 시작...")
result = run_preprocess(input_path, cache_path)
print(f"✅ 전처리 완료 & 캐시 저장: {cache_path}")
return result
| 최적화 전략 | 효과 | 적용 난이도 |
|---|---|---|
| GPU 가속 인코딩 | 5~10배 속도 향상 | ⭐⭐ |
| 병렬 처리 | CPU 코어 수만큼 향상 | ⭐⭐⭐ |
| 스트리밍 처리 | 메모리 사용량 90% 감소 | ⭐⭐ |
| 캐싱 | 반복 작업 100% 스킵 | ⭐⭐ |
| 프리셋 최적화 | 인코딩 속도 2~3배 향상 | ⭐ |
파이프라인 품질 관리 (QC) 자동화
파이프라인이 자동으로 돌아간다고 해서 품질을 방치하면 안 돼.
자동화된 품질 검사(QC, Quality Control)를 파이프라인에 포함시켜야 해.
VMAF / SSIM / PSNR 품질 측정
영상 품질을 수치로 측정하는 지표들이야:
PSNR — 픽셀 단위 오차 측정, 높을수록 좋음 (30dB 이상이면 양호)
SSIM — 구조적 유사도, 0~1 사이 (0.95 이상이면 좋음)
VMAF — 넷플릭스가 개발한 지각적 품질 지표, 0~100 (70 이상이면 좋음)
# FFmpeg으로 VMAF 품질 측정
ffmpeg -i original.mp4 -i compressed.mp4 \
-lavfi "[0:v][1:v]libvmaf=log_fmt=json:log_path=vmaf_result.json" \
-f null -
# SSIM 측정
ffmpeg -i original.mp4 -i compressed.mp4 \
-lavfi "[0:v][1:v]ssim=stats_file=ssim_result.txt" \
-f null -
# PSNR 측정
ffmpeg -i original.mp4 -i compressed.mp4 \
-lavfi "[0:v][1:v]psnr=stats_file=psnr_result.txt" \
-f null -
import json
import subprocess
def quality_check(original_path, processed_path, vmaf_threshold=70):
"""자동 품질 검사"""
result = subprocess.run([
'ffmpeg', '-i', original_path, '-i', processed_path,
'-lavfi', f'[0:v][1:v]libvmaf=log_fmt=json:log_path=/tmp/vmaf.json',
'-f', 'null', '-'
], capture_output=True)
with open('/tmp/vmaf.json', 'r') as f:
vmaf_data = json.load(f)
vmaf_score = vmaf_data['pooled_metrics']['vmaf']['mean']
if vmaf_score >= vmaf_threshold:
print(f"✅ 품질 검사 통과! VMAF: {vmaf_score:.2f}")
return True
else:
print(f"❌ 품질 검사 실패! VMAF: {vmaf_score:.2f} (기준: {vmaf_threshold})")
return False
Apache Airflow — 복잡한 파이프라인 워크플로우 스케줄링
Celery + Redis — 분산 작업 큐 관리
이런 도구들을 파이프라인에 통합하면 운영이 훨씬 편해져!
파이프라인 구축 최종 체크리스트
자, 이제 정리할 시간이야! 🎯
파이프라인 구축할 때 반드시 확인해야 할 것들을 정리했어.
- ☐ 입력 포맷 검증 (지원 포맷 목록 정의)
- ☐ 해상도 정규화 (목표 해상도 및 비율 처리 방식 결정)
- ☐ 색공간 통일 (RGB/YUV 중 파이프라인 표준 결정)
- ☐ FPS 정규화 (목표 FPS 결정, 보간 방식 선택)
- ☐ 오디오 샘플레이트 통일 (44100Hz 또는 48000Hz)
- ☐ 메타데이터 추출 및 보존
- ☐ 손상된 파일 감지 및 처리 로직
- ☐ 코덱 선택 (용도에 맞는 코덱 결정)
- ☐ 비트레이트 설정 (CRF 또는 CBR/VBR 방식)
- ☐ GPU 가속 활용 여부 결정
- ☐ 멀티 해상도 출력 필요 여부 (ABR 스트리밍)
- ☐ 처리 실패 시 재시도 로직 구현
- ☐ 색보정 기준 정의 (LUT 파일 또는 파라미터)
- ☐ 자막 삽입 방식 결정 (하드/소프트 서브)
- ☐ 워터마크 위치 및 투명도 설정
- ☐ 오디오 볼륨 정규화 기준 (-23 LUFS 권장)
- ☐ 썸네일 자동 생성 설정
- ☐ 최종 품질 검사 (VMAF/SSIM 기준값 설정)
- ☐ 에러 로깅 시스템 구축
- ☐ 처리 상태 모니터링 대시보드
- ☐ 실패 알림 시스템 (이메일/슬랙)
- ☐ 중간 파일 자동 정리 로직
- ☐ 처리 속도 벤치마크 기준 설정
- ☐ 스케일 아웃 전략 수립
MVP(최소 기능 제품)부터 시작해서 점진적으로 기능을 추가하는 게 훨씬 현명해.
처음엔 FFmpeg 명령어 몇 줄로 시작해도 충분해! 그리고 재능넷에서 영상 처리 전문가의 도움을 받으면 훨씬 빠르게 고도화할 수 있어 ✨
마무리: 파이프라인 구축의 핵심 마인드셋
오늘 영상 전처리와 후처리 파이프라인에 대해 진짜 깊게 파봤는데 어때? 🤔
처음엔 복잡해 보여도 결국 핵심은 이거야:
2. 모듈화 — 각 단계는 독립적으로 교체 가능해야 해.
3. 자동화 — 사람이 개입 없이 돌아가야 진짜 파이프라인이야.
FFmpeg 하나만 제대로 다뤄도 웬만한 영상 파이프라인은 구축할 수 있어.
거기에 Python으로 자동화 로직을 얹고, 클라우드로 스케일 아웃하면 완성이야.
처음부터 완벽한 파이프라인을 만들려고 하지 말고,
작게 시작해서 점진적으로 발전시키는 게 진짜 프로의 방식이야 ✨
영상 처리 파이프라인 구축이 어렵게 느껴진다면, 전문가의 도움을 받는 것도 현명한 선택이야.
다양한 재능을 가진 전문가들이 모여있는 플랫폼을 활용해보는 것도 좋은 방법이지 ㅋㅋㅋ
자, 이제 직접 파이프라인 만들러 가봐! 화이팅! 🚀🎬
댓글 작성
이 글에 대한 여러분의 생각을 들려주세요
로그인이 필요합니다
댓글을 작성하려면 먼저 로그인해주세요.
댓글 0
지식인의 숲 - 지적 재산권 보호 고지
지적 재산권 보호 고지
- 저작권 및 소유권: 본 컨텐츠는 재능넷의 독점 AI 기술로 생성되었으며, 대한민국 저작권법 및 국제 저작권 협약에 의해 보호됩니다.
- AI 생성 컨텐츠의 법적 지위: 본 AI 생성 컨텐츠는 재능넷의 지적 창작물로 인정되며, 관련 법규에 따라 저작권 보호를 받습니다.
- 사용 제한: 재능넷의 명시적 서면 동의 없이 본 컨텐츠를 복제, 수정, 배포, 또는 상업적으로 활용하는 행위는 엄격히 금지됩니다.
- 데이터 수집 금지: 본 컨텐츠에 대한 무단 스크래핑, 크롤링, 및 자동화된 데이터 수집은 법적 제재의 대상이 됩니다.
- AI 학습 제한: 재능넷의 AI 생성 컨텐츠를 타 AI 모델 학습에 무단 사용하는 행위는 금지되며, 이는 지적 재산권 침해로 간주됩니다.
아직 댓글이 없습니다.