SLOHERO
11

PHOTOSHOT / DEVELOPMENT JOURNAL

LUT 파일을 읽어
사진에 적용하기

컬러 룩업은 정해진 색 변환표를 읽고, 사진의 각 픽셀에 해당하는 출력색을 찾는 기능입니다. 파일을 여는 부분과 색을 계산하는 부분을 나눠 만들었습니다.

조정 레이어 11 · 컬러 룩업
2026.10.01 · SLOHERO · 공개 코드 v0.16.0 기준
보정 전컬러 룩업 보정 전 AI 생성 인물 예시
보정 후컬러 룩업 보정 후 AI 생성 인물 예시
AI 생성 인물 원본에 공개 코드 v0.16.0의 해당 조정만 적용한 결과입니다. 각 글에 설정과 불투명도를 적었습니다. 패널은 기존 릴스 제작 때 캡처한 실제 화면입니다.

11편에서는 LUT가 적용되기까지의 과정을 따라갑니다. 사진에는 자체 제작한 17³ LUT를 사면체 보간으로 적용하고, 디더링을 켠 뒤 불투명도를 75%로 설정했습니다.

  1. 01파일을 읽은 뒤 공통 색상표로 바꿉니다
  2. 02확장자가 같아도 데이터는 검증합니다
  3. 03격자 사이의 색은 두 방식으로 계산합니다
  4. 04ICC는 지원 범위를 따로 정했습니다
  5. 05디더링과 강도 조절을 마지막에 적용합니다
  6. 06파일 읽기와 실제 변환을 따로 검사했습니다
01

파일을 읽은 뒤 공통 색상표로 바꿉니다

예제에 사용한 파일은 Photoshot_Cinematic.cube입니다. 17 × 17 × 17, 총 4,913개의 지점에 출력 RGB가 저장되어 있습니다. 사진의 모든 색을 직접 기록하는 대신 격자 사이의 색은 계산으로 구합니다.

포맷별 파서는 크기, 입력 범위, 출력 배열을 공통 Cube 구조로 바꿉니다. 입력을 먼저 변환하는 1D 곡선이 있으면 shaper에 별도로 보관합니다.

Neutral·Warm Portrait·Cool Shadows·Soft Film은 포토샷에서 만든 프리셋입니다. Photoshop에 포함된 LUT 파일을 그대로 가져온 목록은 아닙니다.

17큐브 LUT와 사면체 보간 디더링 옵션
Photoshot Cinematic · 17³. 파일 로드와 보간 설정이 보이는 실제 패널입니다. · 그림을 누르면 전체 패널을 볼 수 있습니다.
02

확장자가 같아도 데이터는 검증합니다

파일 내용에서 수치를 읽은 다음, 격자 크기와 실제 데이터 개수가 맞는지 확인합니다. 숫자가 아니거나 무한대인 값, 잘못된 입력 범위, 오름차순이 아닌 입력 곡선은 거부합니다.

data.length === size ** 3 * 3
// 격자 한 점마다 R·G·B 세 값
// 입력 범위는 min < max
// 모든 숫자는 유한값인지 확인

위 코드는 검증 조건을 줄여 쓴 것입니다. 실제 파서는 헤더와 각 포맷의 저장 순서도 확인합니다. 입력 파일 한도는 32 MiB, 내부 3D 격자는 한 축당 최대 65입니다.

실제 지원은 포토샷 파서가 허용하는 구조에 한합니다. 확장자만 맞으면 모든 변형을 읽는 것은 아닙니다.
포맷변환 과정
CUBE1D·3D·결합 데이터와 입력 범위 처리
3DL격자 위치·출력 비트 깊이·축 순서 처리
LOOKXML 안의 RGB 부동소수 데이터 해석
CSP입력 곡선과 1D·3D 표 처리
ICC별도 색 관리 엔진에서 65³ 표로 변환
03

격자 사이의 색은 두 방식으로 계산합니다

먼저 입력 곡선을 적용하고, LUT의 최소·최대 입력값에 맞춰 좌표를 구합니다. 좌표의 정수 부분은 격자 위치, 소수 부분은 그 사이에서 얼마나 이동했는지 나타냅니다.

const rx = clamp(
  (cr - cube.min[0]) /
  (cube.max[0] - cube.min[0])
) * (cube.size - 1);
const lr = Math.floor(rx);
const fr = rx - lr;

삼선형 보간은 주변 여덟 꼭짓점을 사용합니다. 사면체 보간은 세 축의 소수 부분을 크기순으로 정렬해 네 꼭짓점을 고릅니다. 두 방식의 결과는 LUT에 따라 달라지므로 어느 쪽이 무조건 원본과 더 같다고 단정하지 않았습니다.

  1. 입력 곡선파일에 shaper가 있으면 먼저 적용합니다.
  2. 격자 좌표입력 범위를 정규화해 셀과 소수 위치를 구합니다.
  3. 보간사면체는 4점, 삼선형은 8점을 가중합합니다.
  4. 마무리디더링과 레이어 불투명도를 적용합니다.
04

ICC는 지원 범위를 따로 정했습니다

ICC는 텍스트 LUT처럼 숫자 목록만 읽을 수 없습니다. 이 부분은 LittleCMS 2.16의 WebAssembly 빌드를 사용했습니다. 파일 헤더와 태그 범위를 확인한 뒤 색 변환을 만들고, sRGB 입력 격자 65³개를 샘플링합니다.

지원 대상은 Lab/XYZ 사이의 Abstract 프로파일과 RGB→RGB Device Link입니다. 일반 모니터 프로파일이나 CMYK Device Link는 이 컬러 룩업에서 받지 않습니다. ICC v2·v4 변환을 다루지만 모든 프로파일을 지원한다는 뜻은 아닙니다.

샘플링한 결과는 이후 다른 LUT와 같은 보간 경로를 사용합니다. 따라서 원래 ICC 변환과 매 픽셀에서 수학적으로 완전히 같은 계산은 아닙니다. 라이브러리와 라이선스는 저장소의 LittleCMS 고지에 함께 남겼습니다.

ICC는 사진 전체의 색 관리 설정을 교체하는 기능이 아니라, 컬러 룩업 입력으로 처리합니다.
대상현재 처리
Lab/XYZ AbstractLittleCMS → sRGB 65³ 샘플링
RGB Device LinkRGB→RGB만 허용
CMYK Device Link거부
모니터·일반 입력 프로파일이 기능에서는 거부
05

디더링과 강도 조절을 마지막에 적용합니다

디더링은 LUT 결과에 매우 작은 값을 더해 8비트 변환에서 경계가 보이는 현상을 완화하려는 옵션입니다. 픽셀 위치를 기준으로 값을 만들기 때문에 화면을 다시 계산해도 같은 위치에 같은 변화가 들어갑니다.

(pixelNoise(pixelIndex) - 0.5) / 255

이 값을 세 채널에 공통으로 더한 뒤 범위를 제한합니다. 레이어 불투명도는 그다음 원래 사진과 섞을 때 적용합니다. 오른쪽 숫자 비교에서는 디더링을 꺼서 LUT와 불투명도 차이만 볼 수 있게 했습니다.

17큐브 LUT의 RGB 표본과 불투명도75퍼센트 계산
그림의 수치는 디더링 끔. 글 상단 사진은 디더링 켬 · 불투명도 75%입니다.
06

파일 읽기와 실제 변환을 따로 검사했습니다

기존 테스트를 다시 실행해 1D+3D 순서, 축 순서, 두 보간 방식, 잘못된 데이터 거부를 확인했습니다. ICC 테스트에서는 항등 Device Link, 반전 Device Link, 항등 Abstract 프로파일을 실제 엔진에 넣었습니다.

이 검사는 ‘파일을 정상적으로 처리하는가’를 확인한 것입니다. 임의의 LUT가 사진을 보기 좋게 만드는지, 모든 Photoshop 설정과 같은 결과인지는 별도의 비교가 필요합니다.

Adobe의 컬러 룩업 안내와 LUT 내보내기 문서는 파일 종류와 기능의 용도를 확인하는 데 참고했습니다. 파서와 보간 코드는 포토샷 저장소, ICC 변환은 LittleCMS를 사용합니다.

2026.10.01 · 공개 코드 v0.16.0 테스트 재실행. Photoshop과의 일치율 측정 결과는 아닙니다.
검사확인 결과
업그레이드 테스트19개 그룹 통과 · LUT 외 기능 포함
ICC 변환항등 Link·반전 Link·항등 Abstract 실행
손상 ICC잘못된 태그 범위 거부

구현 코드와 참고 자료

공개 코드는 구현을 설명하기 위한 참조입니다. 사용·배포 조건은 저장소의 라이선스 표기를 확인하세요. 사진·패널은 자체 제작 자료, 장식 이미지는 AI 생성 이미지입니다.