[블로그 만들기 #6] FFmpeg으로 블로그 썸네일, 이미지들을 예쁘게 다듬어보자.
![Cover Image for [블로그 만들기 #6] FFmpeg으로 블로그 썸네일, 이미지들을 예쁘게 다듬어보자.](/assets/blog/vercel-blog-06/cover.webp)
FFmpeg을 이용하여 블로그에 올라가는 썸네일, 게시글 첨부 이미지들을 개발자답게 다듬어보는 방법을 알아봅시다.
들어가며
이전 포스팅에서 전 세계 웹사이트 데이터를 빨아들이는 괴물 같은 도구인 구글 애널리틱스를, 단순 페이지 뷰 하나 찍어보며 재능을 낭비해봤습니다.
동네 작은 식당이 맛도 없고 지저분하면 다시는 방문하지 않겠죠?
저희처럼 작고 눈에 안 띄는 블로그일수록, 재방문을 유도하기 위해서는 최소한 깔끔하다는 첫인상을 주기 위해서 신경을 써줘야 합니다. (식당 맛에 해당하는 포스팅 품질은 개인의 능력이니 제가 어떻게 해드릴 수는 없습니다)
이번 포스팅에서는 저희 블로그에 올라가는 각종 이미지들을 깔끔하게 다듬기 위해, 개발자들의 Photoshop, FFmpeg 사용법을 알아보도록 하겠습니다.
FFmpeg(Fast Forward mpeg)
FFmpeg은 이미지, 오디오, 비디오 파일을 변환, 자르고, 붙이고, 인코딩하고, 디코딩하고, 스트리밍까지 할 수 있는 멀티미디어 처리의 끝판왕입니다.
오픈소스로 제공되어서 누구나 무료로 가져다 사용할 수 있고, 개발자다운 작명 센스에서부터 빠르다는 것을(Fast Forward) 알 수 있습니다.
ffmpeg의 console.log("hello world")에 해당하는 명령어부터 알아보겠습니다.
ffmpeg -i input.png output.jpg # 참 쉽죠?
뭘 할 수 있나
저희가 FFmpeg을 이용하는 이유는 크게 두 가지입니다.
- 블로그 이미지 통일감: 포스팅에 첨부할 이미지(1920x1080), 비율(16:9), 확장자(.jpg) 표준화
- 썸네일 이미지 통일감: 썸네일 이미지(1200x600), 비율(2:1), 확장자(.jpg) 표준화
이 두가지 목표를 위해 ffmpeg으로 할 수 있는 작업 목록입니다.
- 이미지 리사이징: 다양한 크기의 이미지들을 표준 사이즈인 1920x1080으로 리사이징
- 두 개의 이미지 합성: 원본 이미지에 배경 합성
- 이미지 포맷 변환: 용량 압축을 위해 .png 파일을 .jpg 파일로 변환
- 이미지 crop: 이미지에서 필요한 부분만 잘라내기
이미지들을 다루기 위해서 각종 복잡한 필터 옵션이 들어가지만 걱정하지 마세요. 천천히 하나하나 알아봅시다.
FFmpeg 설치하기
파일 메타데이터도 확인할 수 있도록 ffmpeg, ffprobe 모두 설치해보도록 하겠습니다.
저는 맥을 사용해서 맥 기준으로 설명드리겠습니다.
저는 깔끔한 바이너리 파일을 더 선호해서 homebrew가 아닌, static build 파일을 사용할건데요
FFmpeg 홈페이지 들어가서 Download 페이지 - OS 선택 - static build for macos 버튼 클릭하고
이렇게 최신 버전의(7.1.1) 파일 다운로드 후 압축 해제해 준 다음에
더블클릭해서 ffmpeg 실행하면 이렇게 macos에서 차단하는 화면을 보실 수 있는데요, 이건 애플이 App Store에서 설치한 프로그램이 아니면 모두 나오는 화면인데요
설정 - 개인정보 보호 및 보안 - 그래도 열기 눌러주면
마지막으로 이런 경고창 나오는데 쿨하게 열어주시면 macos에서 더이상 저희 귀찮게 굴 일 없어집니다.
이제 터미널에서 CLI 기반으로 사용하기 위해 PATH 한번 확인해주고
echo $PATH
...:/usr/local/bin:...
sudo mv ~/Downloads/ffmpeg /usr/local/bin/
sudo mv ~/Downloads/ffprobe /usr/local/bin/
원하는 path 폴더로 옮겨주시면 사용할 준비 완료입니다.(터미널에서 파일 실행 경로 인식 못하면 터미널 다시 실행시켜주세요)
이미지 리사이징
이제 본격적으로 ffmpeg을 사용해보겠습니다.
저는 포스팅에서 통일감 있는 느낌을 주기 위해서 모든 게시글 사진들은 1920x1080 사이즈로 표준화해서 업로드하고 있습니다.
테스트를 위해 동일한 16:9 비율인 3840x2160 & PNG 이미지를 1920x1080 & JPG 이미지로 리사이징 후 포맷 변환해서 압축 후 용량을 줄여보겠습니다.
ffmpeg -i origin-2160.png \
-filter_complex "scale=w=1920:h=1080" \
./origin-1080.jpg
이렇게 간단하게 scale 옵션으로 처리할 수 있는데요
원본 이미지가 16:9 비율이 아닌, 1:1 비율인 1024x1024 이미지를 동일한 옵션으로 변환하면 이미지 가로 길이가 쭉 늘어나서 이상한 비율이 돼버립니다.
이걸 방지하기 위해 force_original_aspect_ratio=decrease 옵션으로 변환하고, ffprobe 명령어로 제대로 변환 되었는지 확인해 보겠습니다.
ffmpeg -i input-1024x1024.png \
-filter_complex "scale=w=1920:h=1080:force_original_aspect_ratio=decrease" \
./output-1920x1080.jpg
ffprobe output-1920x1080.jpg
Input #0, image2, from 'output-1920x1080.jpg':
Duration: 00:00:00.04, start: 0.000000, bitrate: 7300 kb/s
Stream #0:0: Video: mjpeg (Baseline), yuvj444p(pc, bt470bg/unknown/unknown), 1080x1080, 25 fps, 25 tbr, 25 tbn
1080x1080 사이즈로 변환된 것을 확인할 수 있는데요, force_original_aspect_ratio=decrease 옵션은 이렇게 원본 비율(1:1)이 깨지지 않는 선에서 저희가 요구한 1920x1080 중 먼저 해당하는 사이즈(높이 1024 이미지를 비율 맞춰서 늘리다보면 높이 1080에 먼저 도달)까지 원본 비율에 맞춰서 늘어난 후 종료됩니다.
저희는 이미지들 통일감 주기 위해서 1920x1080으로 만들어야 하는데, 1080x1080 이미지에서 높이 기준은 충족했으니 나머지 가로 길이(1920 - 1080) 부분에 배경색 넣어서 길이 맞춰주면 되지 않을까요?
이미지 배경 입히기
배경을 넣어주는 필터 옵션 먼저 간단하게 살펴보겠습니다.
-filter_complex "color=c=${hex_color_code}:s=${target_width}x${target_height}"
옵션 먼저 설명하면
- 배경으로 사용할 Color Code: #e6e3df (hex color code 키워드로 검색해서 마음에 드는 색상 선택해 주세요.)
- 배경 가로 길이: 1920 (target_width)
- 배경 세로 길이: 1080 (target_height)
저희가 ffmpeg을 이용하여 작업할 순서는
- 1920x1080 배경 이미지를 먼저 만들고
- 포스팅에 사용할 이미지 파일을 배경 이미지 내부에 들어가도록 비율 깨지지 않게 줄이거나 늘려서
- 비율 맞춰서 줄이거나 늘린 이미지를 배경 이미지 위에 얹은 다음
- 줄이거나 늘린 이미지를 배경 이미지 정중앙에에 배치합니다.
이제 저희가 위에서 사용한 1024x1024 이미지를 1920x1080이라는 표준 사이즈 배경에 비율 깨지지 않게 중앙 정렬해서 넣어보겠습니다.
hex_color_code="#e6e3df" # 사용할 배경색 (옅은 베이지)
target_width="1920" # 표준 출력 파일 가로폭
target_height="1080" # 표준 출력 파일 세로폭
ffmpeg -i "input-1024x1024.png" \
-filter_complex "\
color=c=${hex_color_code}:s=${target_width}x${target_height}[bg]; \
[0:v]scale=w=${target_width}:h=${target_height}:force_original_aspect_ratio=decrease[scaled]; \
[bg][scaled]overlay=(W-w)/2:(H-h)/2:format=auto[out]; \
[out]format=yuv420p,scale=${target_width}:${target_height},setsar=1" \
-frames:v 1 -q:v 2 output-1920x1080.jpg -y
filter_complex 옵션 하나씩 설명드리면
#1920x1080 사이즈의 배경을 만들고 [bg]라는 변수에 담고
"color=c=${hex_color_code}:s=${target_width}x${target_height}[bg];"
# 첫번째 입력 이미지([0:v])를 비율 맞춰서 1920 or 1080 길이 중 먼저 매칭되는 지점까지 비율 맞춰서 조절해준 다음 [scaled]라는 변수에 담아서
"[0:v]scale=w=${target_width}:h=${target_height}:force_original_aspect_ratio=decrease[scaled];"
# 배경(bg) 위에 사이즈 조절된 입력 이미지(scaled) 올려주되, 배경 정중앙에 올리고(overlay=(W-w)/2:(H-h)/2) [out]변수에 담은 후
"[bg][scaled]overlay=(W-w)/2:(H-h)/2:format=auto[out];"
# yuv 옵션으로 색 영역 줄여주고, 혹시 모를 미세한 비율 왜곡을 마지막으로 잡아줍니다 (scale=${width}:${height})
"[out]format=yuv420p,scale=${target_width}:${target_height},setsar=1"
# 최종 파일은 적당한 품질 옵션(q:v 2)으로 jpg 파일로 뽑아주면 완성입니다.
"-frames:v 1 -q:v 2 output-1920x1080.jpg -y"
실전 테스트
바로 실전 테스트 들어가보겠습니다. 테스트 사진은 이상한 변호사 우영우, 하이퍼 나이프를 통해 팬이 된 배우 박은빈님의 사진으로 진행했습니다.
첫번째 샘플 사진으로 가로가 긴 사진(773x515) 먼저 테스트해보면
ffprobe input-01.jpg
Input #0, image2, from 'input-01.jpg':
Duration: 00:00:00.04, start: 0.000000, bitrate: 11738 kb/s
Stream #0:0: Video: mjpeg (Baseline), yuvj420p(pc, bt470bg/unknown/unknown), 773x515 [SAR 1:1 DAR 773:515], 25 fps, 25 tbr, 25 tbn
hex_color_code="#e6e3df" # 사용할 배경색 (옅은 베이지)
target_width="1920" # 표준 출력 파일 가로폭
target_height="1080" # 표준 출력 파일 세로폭
ffmpeg -i "input-01.jpg" \
-filter_complex "\
color=c=${hex_color_code}:s=${target_width}x${target_height}[bg]; \
[0:v]scale=w=${target_width}:h=${target_height}:force_original_aspect_ratio=decrease[scaled]; \
[bg][scaled]overlay=(W-w)/2:(H-h)/2:format=auto[out]; \
[out]format=yuv420p,scale=${target_width}:${target_height},setsar=1" \
-frames:v 1 -q:v 2 "output-01-1920x1080.jpg" -y
Output #0, image2, to 'output-01-1920x1080.jpg':
Metadata:
encoder : Lavf61.7.100
Stream #0:0: Video: mjpeg, yuv420p(pc, bt470bg/unknown/unknown, progressive), 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 200 kb/s, 25 fps, 25 tbn
메타데이터 상으로도 문제 없고
이렇게 가로 빈 공간에 배경화면 잘 들어간 것을 볼 수 있죠?
두번째 테스트로 세로가 긴 사진(773x1160) 변환해보면
ffprobe input-02.jpg
Input #0, image2, from 'input-02.jpg':
Duration: 00:00:00.04, start: 0.000000, bitrate: 17865 kb/s
Stream #0:0: Video: mjpeg (Baseline), yuvj420p(pc, bt470bg/unknown/unknown), 773x1160 [SAR 1:1 DAR 773:1160], 25 fps, 25 tbr, 25 tbn
ffmpeg -i "input-02.jpg" \
.... \ # 옵션 모두 동일
-frames:v 1 -q:v 2 "output-02-1920x1080.jpg" -y
Output #0, image2, to 'output-02-1920x1080.jpg':
Metadata:
encoder : Lavf61.7.100
Stream #0:0: Video: mjpeg, yuv420p(pc, bt470bg/unknown/unknown, progressive), 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 200 kb/s, 25 fps, 25 tbn
1080 높이에 맞춰서 딱 들어가고, 가로 빈 공간(1920-773)에 배경 색상 잘 들어간 것을 확인하실 수 있습니다.
블로그 이미지 통일감 주기
위에서 박은빈님 사진을 예시로 저희는
- 이미지 리사이징
- 두 개의 이미지 합성
- 이미지 포맷 변환 방법
저희 목표 4개 중 3개를 완료했습니다.
이 명령어들 만으로도, 저희는 포스팅에 첨부할 이미지들을
- 통일감을 주면서 (원본 비율 깨지지 않게 1920x1080 리사이징)
- 용량도 낮춰 제공할 수 있습니다. (jpg 파일을 png 파일보다 50% 이상 작게 압축 가능합니다.)
썸네일 이미지 통일감 주기
썸네일 이미지는 게시글 상단에 가장 먼저 표시되는 동시에, 카카오톡, 슬랙 등을 통해서 공유될 때 나오는 open graph 이미지이기도 합니다.
저희는 썸네일 이미지 통일감을 주기 위해서 open graph에서 권장하는 비율은 2:1에 맞춰 1200x600 사이즈로 표준화 화면서 마지막 목표인 이미지 crop 방법을 알아보겠습니다.
사실 이미지 crop 없이 단순 2:1 비율로만 변환이 필요하다면 위에서 진행한 명령어에서
target_width="1200"
target_height="600"
이렇게 target 사이즈만 변경해주면 끝입니다.
하지만 저는 블로그 이전 포스팅 GPT를 이용해 만든 이미지(배경 없는 1024x1024 사이즈의 로고 이미지) 가운데 글자 부분만 crop해서 1200x600 사이즈로 표준화 시켜서 블로그 대표 썸네일로 사용하고 싶은데요
작업 순서 한번 정리해 보겠습니다.
- 1024x1024 사이즈에서 중간 부분만 2:1 비율로 자르기(배경 없음)
- 배경 색 넣어서 합치고
- 1200x600 사이즈로 리사이징(2:1 비율 표준화)
crop 옵션 먼저 정리해보면
input_width=1024
input_height=1024
target_width=$input_width
target_height=$((input_width / 2)) # 2:1 비율 맞춰주려고
-filter_complex "[0:v]crop=${target_width}:${target_height}:(in_w-${target_width})/2:(in_h-${target_height})/2"
해석하면 1024x1024 사진을 1024x512 사이즈로 자를건데 자르는 위치의 좌표가
- 가로(x) 좌표: in_w(1024) - target_width(1024) = 0 => (0)/2 = 0
- 세로(y) 좌표: in_h(1024) - target_height(512) = 512 => (512)/2 => 256
이렇게 시작해야 한다고 알려주는 겁니다.
위에 그림으로 보시면 이해가 빠를텐데요, 최종 파일의 가로 폭은 동일하되, 세로 폭은 원본의 절반이고 원본에서 (0px, 256px) 좌표만큼 이동해서 자르겠다는 의미입니다.
이렇게 crop 옵션을 이용하면 원본 이미지에서 원하는 부분만 깔끔하게 잘라낼 수 있습니다.
보너스
저는 포스팅 첨부 이미지들을 image-01, image-02, ..., image-xx 이렇게 관리하고 있는데, 개발자들은 반복되는 작업은 자동화 시켜야 속이 편안해지는 존재 아니겠습니까?
원본 png 파일들 한번에 배포용 jpg 파일로 배경 입힌 후, 1920x1080 표준 사이즈로 리사이징 위해서
hex_color_code="#e6e3df"
target_width="1920"
target_height="1080"
for input in image-*.png; do
output="${input%.png}"
ffmpeg -i "$input" \
-filter_complex "\
color=c=${hex_color_code}:s=${target_width}x${target_height}[bg]; \
[0:v]scale=w=${target_width}:h=${target_height}:force_original_aspect_ratio=decrease[scaled]; \
[bg][scaled]overlay=(W-w)/2:(H-h)/2:format=auto[out]; \
[out]format=yuv420p,scale=${target_width}:${target_height},setsar=1" \
-frames:v 1 -q:v 2 "${output}.jpg" -y;
done
이렇게 for문 돌려버리면 한번에 표준 사이즈로 변환 할 수 있습니다.
마무리
이번 포스팅에서는 FFmpeg을 이용해서 블로그에 올라가는 모든 이미지들을 통일감 있게 표준화하는 방법을 알아봤습니다.
사실 웹에서 “이미지 리사이징 무료 사이트”만 검색해도 이 정도 작업은 충분히 처리할 수 있습니다. 하지만 FFmpeg은 익숙해지면 딱 한 줄로 귀찮은 반복 작업을 컴퓨터에게 넘겨버릴 수 있죠. 개발자에게 이보다 더 낭만적인 방식이 있을까요?
다음 포스팅에서는 개발자에게 가장 중요하지만, 저희 블로그에서는 아무 스타일 없이 밋밋하게 나오고 있는 코드 블럭을 rehype-highlight 모듈을 이용해 나름 소녀감성 나도록 알록달록 꾸며보겠습니다.