Photoshot development · 02
밝기·대비 응답의 교정: GitHub 참고 구현에서 순서가 있는 응답표 합성까지
픽셀 수치가 어긋난 실패, 이를 고친 실제 코드, 과거 차트 측정과 이번 27개 입력 재현의 구분.
공개 소스 기준: 초기 eead3ca / 후속 v0.16.0·19f45ed · 현재 운영 편집기의 모든 기능을 다룬 논문은 아님
초록
목표는 원본을 유지하면서 밝기·대비를 바꾸고, 마스크·불투명도·문서 저장에 연결되는 조정 레이어였다. Konva의 Contrast 필터를 읽었지만 포토샷은 자체 수식과 채널 조회표를 사용했다. 2026년 9월의 Photoshop 27.3.1 비교에서 중간 근사식의 평균 RGB 채널 오차는 11.505였다. 측정한 밝기 응답 뒤에 대비 응답을 적용하도록 바꾼 뒤 기록된 평균은 0.241로 줄었고, 순서를 반대로 하면 1.150이었다. 이번에는 변경하지 않은 공개 엔진으로 기록된 회색 출력 27개를 재현하고 데이터를 공개한다. 과거 전체 차트를 새로 측정한 결과와 혼동하지 않는다.
1. 목표: 저장 가능한 비파괴 조정 레이어
이번 기능의 목표는 밝기·대비를 편집 가능한 조정값으로 보관하고 원본 픽셀을 유지하며, 프로젝트를 다시 열었을 때 같은 값을 복원하는 것이다. 조정은 기존 마스크·불투명도·레이어 순서에도 참여해야 한다. 슬라이더가 움직여도 실제 픽셀 응답이 틀리면 기능을 완료했다고 볼 수 없다.
8비트 채널에서는 입력 0~255에 대응하는 출력 응답을 미리 계산해 조회표로 만들 수 있다. 조정을 컴파일할 때 이 표를 준비하고 픽셀 처리에서 재사용한다. 이 구조는 첫 구현에 이미 있었다. 나중에 교정하면서 조회표를 처음 도입한 것이 아니라, 표를 채우는 변환과 적용 순서를 바꾼 것이다.1
2. 처음 작성한 코드와 실제로 발생한 수치 오류
9월 18일 기록은 조정 엔진을 쓰기 전에 Konva의 src/filters/Contrast.ts를 열어 읽었음을 보여준다. 포토샷의 첫 수식은 밝기를 흰색 또는 검정 쪽으로 이동시킨 뒤 정규화 중간값 0.5를 중심으로 대비를 적용한다. 이 계산은 과거 저장 문서의 동작을 보존하는 이전 revision 분기로 공개 소스에 남아 있다. Konva 수식과 동일한 구현은 아니다.
9월 25일에는 개발 중간의 일반 모드 근사식을 Photoshop 27.3.1과 비교했다. 밝기 +30·대비 +20에서 평균 RGB 채널 절대 차이는 11.505, 최대는 25였다. 회색 입력 0은 정답 0 대신 25, 입력 128은 157 대신 168이 나왔다. 실제 실행 기록에 있는 실패다. 단, 9월 18일 첫 Legacy 수식을 나중에 측정한 값이 아니라 그 비교 당시 중간 버전의 결과다.2
19f45ed if (t === 'brightness' && a.revision !== 2) {
x = v.brightness < 0 ? x * (1 + v.brightness / 100) : x + ((1 - x) * v.brightness) / 100;
const contrast = v.contrast < 0 ? 1 + v.contrast / 100 : 1 / (1 - v.contrast / 101);
x = (x - 0.5) * contrast + 0.5;
}3. GitHub 자료가 제공한 것과 제공하지 않은 것
Konva 코드는 채널을 정규화하고 중간값을 뺀 뒤 대비 계수를 곱하고, 다시 이동·제한한다. 실제 픽셀 루프를 읽으면 입력 범위·채널 처리·알파를 건드리지 않는 경계를 확인할 수 있다. 아래의 짧은 외부 발췌는 대비 계수와 빨강 채널 계산 일부이며 포토샷의 운영 코드가 아니다.3
자료가 뒷받침하는 것은 소스 참고와 별도 구현이다. Konva 필터를 그대로 설치하거나 복사했다고 쓰지 않는다. 공개 구현도 다른 응답 곡선을 목표로 할 수 있다. 포토샷은 자체 수식을 만든 뒤 실제 측정 데이터로 수정했다. 관찰된 불일치의 해결책은 “참고 코드가 Photoshop과 같을 것”이라는 가정이 아니라, 교정 자료와 제품 구조에 맞춘 연결이었다.
const adjust = Math.pow((this.contrast() + 100) / 100, 2);
// …
red -= 0.5;
red *= adjust;
red += 0.5;| Source | Use category | Contribution | Decision |
|---|---|---|---|
| Konva Contrast.ts | 읽은 GitHub 소스 | 중간값 중심 대비 픽셀 루프 | 참고; 그대로 설치하지 않음 |
| 초기 포토샷 엔진 | 직접 작성 수식 | 밝기→대비; 256칸 조회표 | 이전 revision 보존 |
| Photoshop 교정 | 측정 응답 데이터 | 제어값별 회색 램프 출력 | Adobe 소스 코드 아님 |
| 포토샷 revision 2 | 직접 작성·수정 | 보간과 순서 있는 응답 합성 | 기존 레이어 구조에 연결 |
9월 18일 조회 → 자체 수식 작성 → 9월 25일 비교·교정 순서다. 실패 후 Konva를 처음 발견한 것으로 각색하지 않았다.
4. 해결 코드: 응답 데이터·보간·적용 순서
교정에서는 Photoshop 일반 모드에 합성 회색 램프를 넣었다. 다른 값을 0으로 고정한 채 밝기 −150~+150, 대비 −50~+100을 각각 10 간격으로 바꿔 측정했다. 결과 응답표는 tone-response.ts에 보존돼 있다. sampleResponse가 중간 슬라이더 값과 채널 위치를 보간하며, 컴파일한 RGB 표는 실수 응답을 저장한다.
revision 2의 일반 모드는 밝기를 먼저, 대비를 나중에 적용한다. 보통 C(B(x))와 B(C(x))는 같지 않으므로 순서 자체가 구현의 일부다. 두 호출은 기존 채널별 256칸 컴파일 경로 안에 들어가고, 마스크·불투명도는 기존 합성기가 처리한다. 이전 revision 분기와 별도의 Legacy 옵션도 일반 모드 응답표와 구분해서 유지한다.14
19f45ed x = sampleResponse(toneResponse.brightness, v.brightness, -150, x);
x = sampleResponse(toneResponse.contrast, v.contrast, -50, x);19f45ed const tables = ['r', 'g', 'b'].map((channel) =>
Float64Array.from({ length: 256 }, (_, i) => {
let x = i / 255;5. 측정 방법과 결과: 과거 기록과 이번 재현
과거 시험 차트는 256×32픽셀이며 상단 8줄은 회색 램프, 나머지는 혼합 RGB다. 알파를 제외한 24,576개 채널 값을 비교했다. 기록된 순서 실험에서 밝기→대비는 평균 0.2412109375·최대 1, 대비→밝기는 평균 1.1497395833·최대 4였다. 이어 수정 엔진 실행은 평균 0.241·최대 1·완전 일치 채널 75.88%를 기록했다. 반올림된 값은 같은 명시 조건에 대한 결과다.
이번 개정에서는 변경하지 않은 공개 엔진의 응답표를 과거 교정 JSON과 대조해 전체 일치를 확인했다. 또한 +30/+20, +17/+13, −65/−23을 컴파일한 뒤 각 조건의 회색 입력 9개씩을 평가했다. 현재 출력 27개는 모두 과거 포토샷 출력과 같았다. 비교에 사용하는 Photoshop 값은 새로 내보낸 값이 아니라 과거 값이다. 확인한 폴더에 전체 차트 PNG가 없어 전체 오차를 오늘 다시 계산하지는 않았다.25
| Implementation / setting | Mean |Δ| | Max |Δ| | Origin |
|---|---|---|---|
| 중간 근사식 +30/+20 | 11.505 | 25 | 과거 측정 |
| 응답 밝기→대비 +30/+20 | 0.241211 | 1 | 과거 측정 |
| 응답 대비→밝기 +30/+20 | 1.149740 | 4 | 과거 측정 |
| 추가 조건 +17/+13 | 0.215 | 1 | 과거 측정 |
| 추가 조건 −65/−23 | 0.229 | 1 | 과거 측정 |
열 순서: 구현/조건, 평균 채널 절대 차이, 최대 차이, 측정 구분. 이번 실행: 응답표가 과거 데이터와 일치하고 회색 출력 27/27개가 과거 포토샷 출력과 같다. 이 27개로 전체 차트 평균을 다시 추정하지 않았다.
6. 결과의 의미와 남은 검증
기록은 특정 차트에서 발생한 수치 오류가 교정됐고, 보존된 회색 출력이 재현됨을 보여준다. 그 비교에서 평균 오차는 약 97.9% 줄었지만, 이것을 시각 품질 점수나 전체 호환성 비율로 쓰지 않는다. 독립적인 반복 표본이 아닌 결정적 차트 비교이므로 신뢰구간을 만들지 않았다. 렌더링 속도 향상도 측정하지 않았다.
후속 인물 예시는 밝기 +18·대비 +12로 설정돼 있다. 이를 +30/+20 차트 수치의 시각적 증거로 사용하지 않는다. 새 Photoshop 출력으로 전체 픽셀을 다시 비교하기, 다른 색상 프로파일 검증, 브라우저 저장·내보내기 확인은 각각 별도 과제다. 3편에서는 같은 조정 구조를 레벨과 히스토그램 제어로 확장한다.
코드·데이터와 재현 자료
보존한 측정 요약, 이번 재현 CSV, 정확한 소스를 내려받아 확인할 수 있다. 새 Photoshop 전체 비교에 필요한 원 차트 PNG는 해당 폴더에 없었다. 재현 하네스는 현재 소스를 과거 값과 대조한다.