RGB 값을 255로 나눠 정규화해야 할까, 256으로 나눠야 할까?
(30fps.net)- 8비트 정수 색상을 부동소수점으로 변환할 때 255로 나누는 표준 방식과 0.5 바이어스 후 256으로 나누는 대안 방식의 차이
- 255 방식은 정수 0을 0.0, 255를 1.0에 매핑해 검은색과 흰색을 직접 다루기 쉽고, GPU의 UNORM-to-float 변환 방식과도 맞음
- 256 방식은
(img + 0.5) / 256.0으로 각 값을 구간 중앙에 놓아 디더링 같은 작업에서 경계 처리를 단순하게 만들 수 있지만, 0이 0.0이 아니어서 처리 로직이 8비트 입력에 묶임 - 255 방식은 양 끝 구간이 절반 폭이라 균일한
[0, 1]난수를 다시 8비트로 반올림하면 0과 255가 다른 값보다 절반 빈도로 나오지만, 실제 이미지 왕복 변환은 손실 없이 동작함 - 타인의 이미지를 처리한다면 255로 정규화가 정답, 저장·로딩을 모두 통제하는 경우에만 256 방식 고려 가능
문제 설정
- 이미지를 받아 부동소수점으로 변환, 처리 후 다시 8비트 색상으로 저장하는 프로그램에서 정수-부동소수점 변환 방식이 쟁점
- 두 가지 접근법 존재
- 표준 방식 (255로 나눔):
pixels = img / 255.0→ 처리 →output = np.trunc(result * 255 + 0.5) - 대안 방식 (256으로 나눔):
pixels = (img + 0.5) / 256.0→ 처리 →output = np.trunc(result * 256) - 두 경우 모두 최종 형변환 전 값을 0~255로 제한함:
output.clip(0, 255).astype(np.uint8)
- 표준 방식 (255로 나눔):
- 표준 방식은 정수 0을 0.0, 255를 1.0에 매핑, GPU의 UNORM-to-float 변환 방식과 같음
- 대안 방식은 0.5 바이어스를 더해 정수 0이
0.5/256 = 0.001953125에 매핑- 이로 인해 이 상수를 모르면 검은 픽셀을 감지할 수 없음
- 부동소수점 계산을 하더라도 로직이 8비트 입력에 묶이게 됨
- 표준 방식에서는 항상 검은색을 0.0으로 가정 가능
255.0에 대한 반론
- 표준 방식을 수직선 위에 그리면 다소 이상하게 보임
-
양쪽 끝에 더 작은 빈(Bin, 칸)이 있음
- 표준 공식의 양 끝 빈이 [0,1] 범위 밖으로 튀어나옴, 범위가 "늘어난(stretched)" 형태
- 부동소수점을 정수로 되돌릴 때 양 끝 빈의 폭이 다른 빈의 절반에 불과
- 알고리듬에서 극단값을 출력하기가 "더 어려워짐"
- 균일한 [0,1] 노이즈를 생성해 표준 공식으로 반올림하면 0과 255 값이 다른 정수보다 절반 빈도로만 발생
- 100만 개 균일 난수의 히스토그램을 보면 0과 255 빈이 다른 빈의 절반 높이임을 확인 가능
- 다만 극단값 회피 편향이 실제 문제가 되는 상황은 떠올리기 어려움
- 원본 이미지는 여전히 손실 없이 왕복 변환(uint8 → float → uint8) 됨
- 0.0이나 1.0을 살짝 벗어난 결과도 올바른 빈으로 반올림되어 출력 분포가 균등화
- 예: 처리 과정에서 색상에 0.005를 빼는 경우, 표준 방식은 검은색이 0 미만으로 내려가고 대안 방식은 양수 유지하지만 두 방식 모두 최종적으로 정수 0 출력
-
부정확성
- 표준 방식의 부동소수점 값은 정확하지 않음, 예:
128/255.0 ≈ 0.501961이지만128/256.0 = 0.5 - 반올림 오차로 부동소수점 값 간 거리가 미세하게 변동하나 오차가 극히 작아 실질 문제 아님
- 32비트 부동소수점은 23비트 가수를 가지며, 오차는 최하위 비트 수준으로
2⁻²³미만 - 상대 오차 0.00001%는 정교한 이미지 처리에서도 무의미, 비정확성은 기술이 아닌 미적 문제
- 32비트 부동소수점은 23비트 가수를 가지며, 오차는 최하위 비트 수준으로
- 표준 방식의 부동소수점 값은 정확하지 않음, 예:
-
정수 범위에 속하지 않는 값
- 대안 방식은 각 부동소수점 값을 두 정수의 정확히 중간에 배치
- 원래 양자화 값을 알 수 없으므로 연속된 두 정수의 평균점이 좋은 추정값이라는 절충
- 디더링이 더 편리하다는 주장 존재 (Andrew Kesler의 2015년 블로그 글 "Converting Color Depth")
- 엣지 케이스 걱정 없이 노이즈 추가 가능
- 반대로 표준 공식의 어색한 극단값은 노이즈 분포 일관성 유지를 위해 세심한 처리 필요
- 대안 방식은 각 부동소수점 값을 두 정수의 정확히 중간에 배치
두 종류의 양자화기
- 두 접근법을 균일 스칼라 양자화기(uniform scalar quantizer) 두 종류로 볼 수 있음
- Wikipedia 양자화 문서에 따르면 부호 있는 입력 데이터의 균일 양자화기는 두 유형으로 분류
- mid-tread: 0을 0 값 재구성 레벨에 매핑 (계단의 디딤판에 해당)
- mid-riser: 0을 0 값 분류 임계값에 매핑 (계단의 챌면에 해당)
- Wikipedia는 출처로 1977년 논문(Allen Gresho, "Quantization")을 인용
- 양자화기 공식 (L은 출력 레벨 수, 예: 256)
- mid-tread 계단 양자화기: 인코딩
k = trunc(xL + 0.5), 디코딩yₖ = k/L - mid-riser 계단 양자화기: 인코딩
k = trunc(xL), 디코딩yₖ = (k+0.5)/L
- mid-tread 계단 양자화기: 인코딩
- 두 방식에 적용 시
- 표준 공식 = mid-tread (L=255)
- 대안 공식 = mid-riser (L=256)
- 표준 방식은 부호 없는 입력에 mid-tread를 쓰면서 L=255 코드를 택한 조합, 8비트 입력에 최적이 아님
- 양 끝을 0.0과 1.0에 매핑하는 프로그래밍 편의를 위한 선택
-
더 높은 양자화 오차, 그러나 실제로는 아님
- 균일 분포 실수 x∈[0,1]을 8비트 정수로 인코딩 후 다시 실수로 재구성하는 시스템이라면 표준 공식은 대역폭 낭비
- 표준 방식의 표현 가능 범위는
[-0.5/255, 255.5/255]로 [0,1] 입력에 필요 이상으로 넓어 재구성 오차 증가 - StackOverflow 사용자 Peter Mudrievskij 계산에 따르면 평균 절대 오차는 255 제수가
1/1020, 256 제수가1/1024, 따라서 256으로 나누는 쪽이 이론상 더 정밀
- 표준 방식의 표현 가능 범위는
- 그러나 실제로는 이러한 재구성을 수행하는 것이 아님
- 8비트 RGB 이미지를 불러와 처리 후 다시 저장하는 전제, 저장 시 양자화 방식을 통제할 수 없으며 손실된 정보는 영구히 사라짐
- 이미지가 표준 공식으로 곱하고 반올림되어 저장되었다면, 로드 시 256으로 나눠도 정밀도 복원 불가
- 저장과 로드를 모두 통제할 때만 낮은 재구성 오차 주장이 의미를 가짐
- 타인의 이미지를 대안 공식으로 로드하면 오히려 더 많은 오차 유발
- 대부분 표준 공식으로 양자화되었을 가능성이 높아 잘못된 스케일로 디코딩하면 이론상 부정확
- 실제로는 색상이 절대 측정값이 아니므로 약간 작은 범위에서 작은 오프셋을 두고 처리하는 정도
- 두 양자화기의 인코딩과 디코딩 단계를 섞어선 안 됨, 흔히 저지르기 쉬운 깨진 코드
- 균일 분포 실수 x∈[0,1]을 8비트 정수로 인코딩 후 다시 실수로 재구성하는 시스템이라면 표준 공식은 대역폭 낭비
결론
- 낯선 사람이 준 이미지를 처리한다면 RGB 값을 255로 정규화해야 함
- 부정확한 부동소수점 값이나 추상적인 재구성 오차 우려는 대안 선택의 좋은 이유가 아님
- 이미지 저장과 로딩을 모두 통제하고, 0을 0에 매핑할 필요가 없으며, 처리 코드를 8비트 동적 범위에 묶어도 괜찮다면 256으로 나눠 정밀도를 약간 더 확보 가능
- 단, 동료가 표준 공식으로 이미지를 로드해 계획을 망칠 수 있음에 유의
다른 견해들
- Jonathan Blow의 2002년 글은 mid-riser와 mid-tread 양자화기를 이름 없이 다룸, 다이어그램 아이디어의 출처
- Andrew Kesler의 2015년 블로그 글은 대안 공식을 옹호
- 다만 비교 대상이 반올림 없는 표준 공식이어서 분석 대부분이 무효화
댓글과 토론
Hacker News 의견들
-
색상 값이 정확히 무엇을 뜻하느냐는 성분당 8비트에서는 대체로 별일 아님. 분모가 255냐 256이냐 차이로 생기는 오차가 아주 작고, 차이를 보려면 색 감각이 좋고 화면에 아주 가까이 가야 하며, 모니터나 휴대폰 화면도 보통 보정되어 있지 않기 때문임
하지만 마이크로컨트롤러로 VGA 신호를 만들고 색 출력 핀이 8개(빨강 3, 초록 3, 파랑 2)뿐이면 꽤 골치 아파짐. 이때 색상 값은 VGA 모니터에 보내야 하는 0V~0.7V 전압 레벨 그 자체임
파랑 채널은 0→0V, 1→0.23V, 2→0.47V, 3→0.7V로 매핑되고, 빨강/초록은 0→0V, 1→0.1V, …, 7→0.7V로 매핑됨. 양 끝을 빼면 파랑 전압이 빨강/초록 전압과 하나도 맞지 않아서 순수한 회색을 볼 수 없고, 가장 가까운 색도 차이 방향에 따라 파란색이나 노란색 기가 약간 섞임
게다가 파랑을 다른 채널과 섞는 거의 모든 그라데이션도 어긋나 보임. 예를 들어 순수 빨강에서 순수 흰색으로 가는 선상의 가장 가까운 색들이 조금 주황색이나 보라색처럼 보임
Raspberry Pi Pico 2에서 이중 버퍼 320x240 프레임버퍼로 8비트 색 VGA 출력을 하는 코드는 여기 있음: https://github.com/moefh/pico-vga-8bit-demo- 어릴 때 노이즈 낀 CRT 화면을 보면서 가장자리의 희미한 파란색과 노란색 선을 봤던 기억이 남. 왜 하필 그 두 색인지 늘 궁금했는데, 같은 원인이라면 이제야 알게 된 셈임
- 감마 보정을 빼먹었음. 값을 0~255 범위에서 전압으로 바꾸기 전에 PC는 보통 그 값을 2.2제곱으로 올림
이러면 작은 값과 큰 값의 차이가 훨씬 더 두드러짐: 2^2.2 = 4.595, 255^2.2 = 196,964.699 - 이 문제에는 시간 방향 디더링이 가장 좋아 보임. 픽셀별 델타-시그마 변조는 꽤 쉽게 할 수 있음
30Hz로 바뀐다면 사람이 약간 파란색과 약간 노란색의 차이를 구분하기는 어려울 듯함 - 그래서 80년대에 RGBI 색상이 그렇게 흔했나 봄
-
255를 지지하는 논거로, 흑백 이미지라는 극단적 사례를 보면 됨. 단일 비트에서 0은 검정, 1은 흰색임
0은 0.0에, 1은 1.0에 매핑되어야 한다는 건 꽤 명확함. 흑백이지 밝은 회색(0.25)과 어두운 회색(0.75)이 아니기 때문임. 즉 흑백 이미지는 2가 아니라 1로 정규화함
2비트라면 보통 0=검정, 1=밝은 회색, 2=어두운 회색, 3=흰색이므로 0.0, 0.33, 0.66, 1.0으로 매핑하는 게 자연스러움. 검정은 검정이고 흰색은 흰색이어야 하며 간격도 같아야 하니 3으로 정규화함
이 논리를 8비트까지 이어가면 255로 정규화하게 됨. 8비트에서는 차이가 아주 작아지더라도 검정은 0.0, 흰색은 1.0이어야 하기 때문임
다른 방식인 8비트에서 256 정규화를 쓰면 출력 범위가 비트 수에 따라 달라짐. 1비트는 [0.25, 0.75], 2비트는 [0.125, 0.875]처럼 됨. 보통 원하는 건 비트 수가 늘수록 뉘앙스가 늘어나는 것이지, 대비가 달라지는 게 아님 -
정말 생각할 거리를 주는 글이었고, 개인적으로 갖고 있던 전제를 다시 따져보게 됨
전기공학 배경에서 보면 글의 “두 종류의 양자화기” 제시는 동의하기 어려움. 수학적으로는 엄밀하지만 실제 시스템에 기반한 설명은 아님
ADC에는 항상 본질적으로 ±1/2 LSB 양자화 불확실성이 있음. 전달 특성은 항상 mid-tread 샘플링이고, 적어도 반례를 본 적은 없음. 이는 양극성 ADC든 단극성 ADC든 마찬가지임
최저 코드는 음전압 기준이고 최고 코드는 양전압 기준임. 전달 특성 그래프는 글에서 보인 것처럼 최고/최저 구간이 사실상 1/2 LSB 폭이라는 점을 보여줌
단극성 시스템에서는 중간 전압을 정확히 표현할 수 없고, 다시 말해 회색 문제가 생김. 양극성 시스템에서는 0V가 mid-tread의 N/2 값이지만, 그렇다고 “256개 구간”이 있다는 뜻은 아님
그래서 (VREF+ - VREF-) * k / (2^N - 1)을 계속 쓸 것임. 즉 255 정규화에 동의함. 결국 울타리 기둥 오류와 같아서, 값은 N개지만 구간은 N-1개임. 값보다 구간이 적다면 구간 하나를 두 값 사이에 나눠야 하고, 그래서 끝점에 1/2 LSB 구간이 생김- 내가 본 모든 ADC 문서는 양의 풀스케일을 표현할 수 없다고 적고 있음. 예를 들어 8비트 ±1V ADC에서 -128은 -1V를 뜻하고, +127은 127/128=0.99219V를 뜻함
126에서 127로 넘어가는 전이는 양의 전체 범위에서 1.5 LSB 떨어진 지점에서 일어남. 1 LSB 차이는 2/255=0.00784V가 아니라 1/128=0.00781V 차이를 의미함
하지만 실제로 전압과 불확실성이 중요하다면 이런 차이는 대부분 별 의미가 없음. 기준 전압에 바이어스가 있고 선형성 오류도 있음. 1 LSB는 1/128에도 2/255에도 정확히 맞지 않으며, 보정을 위한 매개변수가 필요해짐
- 내가 본 모든 ADC 문서는 양의 풀스케일을 표현할 수 없다고 적고 있음. 예를 들어 8비트 ±1V ADC에서 -128은 -1V를 뜻하고, +127은 127/128=0.99219V를 뜻함
-
이건 과학 계산에서 말하는 노드 중심 샘플과 셀 중심 샘플의 차이를 1차원으로 본 것과 비슷함. 값이 구간의 가운데(또는 삼각형/사면체의 가운데)에 있는지, 구간 경계(또는 삼각형/사면체의 꼭짓점)에 있는지를 정해야 함
과학 계산에서는 값을 어떻게 해석해야 하는지 모른 채 데이터 처리를 시작하는 건 말이 안 됨. 오디오 신호 처리에서도 정수 스트림만 받았다면 그 정수가 어떤 표현 의도를 갖는지, 예를 들어 mu-law 인코딩인지 선형인지 알아야 원 신호에 대해 계산할 수 있음. 값에 붙은 메타데이터가 그 답을 제공해주길 기대하게 됨
하지만 8비트 픽셀 값에서는 표현 의도를 전달할 수 있는 제대로 된 파일 형식의 메타데이터가 없다면 표류하게 되고, 정답은 없음. 글쓴이가 말하듯 자기 용도에서 더 좋은 결과를 내는 쪽을 고른다고 해서 비난할 수는 없지만, 맥락 없는 비트는 의미가 훼손된다는 점은 알릴 수 있음- ESA의 Sentinel-2 level-2 위성 이미지 양자화에서 쓰는 정규화 값이 떠오름
대략 이런 식임: Digital Number DN=0은 “NO_DATA” 값으로 남고, DN이 [1; 1;215-1] 범위일 때 L2A SR 반사율 값은 L2A_SRi = (L2A_DNi + BOA_ADD_OFFSETi) / QUANTIFICATION_VALUE가 됨
https://sentiwiki.copernicus.eu/web/s2-products
- ESA의 Sentinel-2 level-2 위성 이미지 양자화에서 쓰는 정규화 값이 떠오름
-
여기에는 0부터 255까지 256단계가 있다고 가정하는 오류가 있음. 사실 8비트로 표현할 수 있는 값이 256개이고, 0(검정)에서 255(순수 흰색)까지의 간격은 255개임
그래서 255로 나누는 건 문제가 아님. 물론 128이 정확한 반 회색은 아니고, 0~255의 양자화된 8비트 값은 거의 항상 선형 지각 공간이 아니라 sRGB에 있음
현대 API에서 샘플링 위치를 다룰 때도 비슷한 혼동이 생김. 위치가 픽셀 중심이 아니라 좌표로 지정되기 때문임- BeOS API는 픽셀 중심 기준임. 이제 아무도 신경 쓰지 않겠지만
-
대수적으로 보면 답은 명확히 f(x) -> [0, 255]임
f(n * 0) == n * f(0)이 성립하지 않으면 이상한 일이 벌어짐. 예를 들어 f(x) -> [0, 255]라면 f(0) + f(0) + f(0) = 0 + 0 + 0 = 0 = f(0)임
반면 f(x) -> [0.5/8, 7.5/8]라면 f(0) + f(0) + f(0) = 0.5/8 + 0.5/8 + 0.5/8 = 1.5/8 != f(0)이 됨
뒤쪽을 택하면 x 쪽에서 한 계산과 f(x) 쪽에서 한 계산이 서로 맞아떨어지리라고 기대할 수 없음. 즉 대수적 대응이 깨짐 -
+0.5 해법을 지지하고 싶음. 첫째로 가장자리의 반쪽 크기 구간이 마음에 들지 않고, 둘째로 255 기반 표현은 보통 HDR이 아니라 SDR 이미지임
RGB 값은 어떤 적응 상태에 대한 휘도를 나타내며, 낮 장면의 “0”은 “휘도 0”이 아님. 가장 밝은 지점의 약 0.001배 정도일 뿐이고, 광자는 수백만 개라 0보다 훨씬 많음
어떤 의미에서 눈은 대비를 미끄러지는 척도로 경험하며, 시스템 안에 절대적인 0은 없음. 예를 들어 방송 시스템은 역사적으로 SDR 휘도 범위로 16~235를 썼음. “반드시 0이 있어야 한다”는 논리는 편향이 생긴다고 보고, 대부분의 경우 0은 필요하지 않다고 생각함- VFX용 이미지 처리와 렌더링을 많이 해본 입장에서, 이 뒤에 색공간 변환이 일어난다는 점을 잊은 것 같음. 예전 SDR에서는 sRGB의 선형 Rec.709로, 최신 형식에서는 더 넓은 색역으로 변환하는 식임. 따라서 동적 범위가 눌리는 일은 로드 이후에 일어남
또한 이미지 처리와 합성 워크플로 상당수는 옳든 그르든 0이 0을 뜻한다고 가정함. 그래서 8비트에서 0u는 0.0f로, 255는 1.0f로 매핑된다고 보는 경우가 많음. 마스크나 알파에서 0 값이 0.0을 조금 넘게 되면, 어딘가의 코드가 0.0 하드 임계값으로 다른 연산을 마스킹하면서 아티팩트가 생김. 반대로 알파에서 255가 더 이상 1.0f가 아니면 사전 곱셈 후 객체가 아주 약간 투명해짐
같은 일이 +0.5 때문에 마스킹에서 254가 1.0f가 될 때도 생길 수 있음 - 글은 RGB에 초점을 맞추지만, 같은 양자화 문제는 이산 표현과 연속 표현 사이에 매핑되는 모든 종류의 신호에 존재함
핵심은 광자 0개를 표현하느냐가 아니라, 1바이트에 저장되는 정보를 최대화하느냐임. 이상적으로는 바이트 값 0을 덜 쓰면 안 되고, 0번째 버킷에 들어갔어야 할 데이터에 바이어스를 더해서도 안 됨. 밝음에서 매우 밝음으로 가는 색공간이라도, 모든 바이트가 밝기 범위의 같은 크기 조각을 나타내게 해야 함 - 역사적으로 방송 시스템이 SDR 휘도 범위로 16~235를 썼다는 게 문제임. 안타깝게도 “현대적” HDMI도 아직 이 이상한 관행에 시달려서, 디스플레이와 소스가 합의하지 않으면 화면이 씻긴 듯 보이거나 검은색이 뭉개짐
- 두 해법 모두 0.5를 더함. 차이는 그 일이 과정의 어디에서 일어나느냐임
- 흥미로운 아이디어지만 세상이 흔들리는 느낌임. 처리 프로그램 입장에서는 예전의 검정(0.0)과 흰색(1.0)이 아주 어두운 회색과 아주 밝은 회색이 되어버림
- VFX용 이미지 처리와 렌더링을 많이 해본 입장에서, 이 뒤에 색공간 변환이 일어난다는 점을 잊은 것 같음. 예전 SDR에서는 sRGB의 선형 Rec.709로, 최신 형식에서는 더 넓은 색역으로 변환하는 식임. 따라서 동적 범위가 눌리는 일은 로드 이후에 일어남
-
자가 12인치까지 있다면, 자 위의 점 개수인 13이 아니라 길이 L로 정규화해야 함
- 그 비유가 헷갈림. “자”가 0~255로 표시된 256개의 점을 가진 255인치 자인지, 아니면 1인치 구간 256개를 가진 256인치 자라서 L = 256×1인지 모르겠음
- 실제로 세려는 게 울타리 기둥이라면 울타리 기둥 오류는 오류가 아님
- 맞지만
>> 8이 훨씬 빠름 - 숫자가 점을 나타낸다고 누가 정했나? 점 사이의 구간을 나타내는 걸 수도 있음
- 내가 멍청한 건가. 0은 시작점에서 시작하지 않나?
-
한동안 생각하지 않았던 주제를 다뤄서 읽기 즐거운 글이었음. 게임 개발에서 게임 로직은 부동소수점 수학을 쓰는데 픽셀 아트는 정수 좌표에 그려야 했던 순간들이 떠올랐음
몇 군데에서 +0.5와 비슷한 방식을 써서 덜 이상해 보이게 하려 했음. 특히 움직이는 카메라가 있을 때 그랬고, 카메라도 잘라내야 했음
아래에 링크된 Jonathan Blow의 2002년 글 [1]도 재미있었음. 첫 번째 글의 시각화가 더 깊이 들어갈 때 많은 도움이 됨
[1] https://web.archive.org/web/20240706043551/https://number-no...
Lobste.rs 의견들
- 지저분해 보이지만 맞음, 값은 255가 맞음
직관적이지 않다면 2비트 퇴화 사례로 보면 됨. 가능한 정수 값이 0, 1, 2, 3뿐일 때 정수→부동소수점 변환을 전부 계산해 보면, 검정/흰색이 검정/흰색이 아니거나 간격이 명백히 고르지 않은 이상한 동작을 피하려면 0.0, 0.33..., 0.66..., 1.0이 됨
따라서 역변환은 4(2^2)가 아니라 3을 곱하는 방식이 됨- 앞부분은 맞지만, 거기서 “역변환은 3을 곱해야지 4가 아니다”가 따라오지는 않음
역변환에는 양자화(반올림) 가 필요하고, 바로 그 지점이 대칭성을 깨는 핵심임
0..=1 범위의 균등한 실수 그라디언트를 만들어 0, 1, 2, 3으로 양자화해 보면, 3을 곱하면 결과가 고르지 않다는 걸 알 수 있음. ×3 후round()는 1과 2가 과대표현되고, ×3 후floor나ceil은 0이나 3을 특이점처럼 접어 넣어서 그라디언트가 4색 중 3색만 쓰는 것처럼 보이게 만듦
/3과×3논리는 정확한 숫자를 왕복 변환할 때는 괜찮아 보이지만, 중간값은 반올림 선택에 크게 영향을 받고 데이터 처리를 시작하는 순간 중요해짐
정수 비율이 균등해지는 건 (4-ε)를 곱하고 내림할 때뿐이며, 이는 ×4,floor(),clamp()와 같음. 이상한 1 차이 또는 ε 차이 오류처럼 느껴지지만, 직관적으로는 가장 보기 좋은 해법임
- 앞부분은 맞지만, 거기서 “역변환은 3을 곱해야지 4가 아니다”가 따라오지는 않음
- 제목 때문에 많이 헷갈렸음. 일부러 그런 건지 모르겠지만, 결국 “0..1이 [0..255.0] 에 대응하나, [0.5..255.5] 에 대응하나?”처럼 보임
내게 답은 늘 “당연히” [0.0..255.0]이었지만, 아마 모두에게 당연한 건 아닌 듯함
글에서는 “극단” 구간이 다른 구간의 절반 용량만 가진다고 하는데, 이 프레이밍도 맞지 않다고 봄
[0..1] 밖의 값이 존재하지 않는다면 좁은 구간처럼 보이는 건 렌더링 산물임. 범위를 벗어난 값이 없다는 지식을 가지고 버킷을 잘라냈기 때문에 더 좁게 렌더링된 것뿐임
반대로 [0..1] 밖의 값이 존재한다면 그 범위는 무한함. 글은 후자는 인정하지만 전자는 인정하지 않음
첫 번째를 인정하는 순간 올바른 동작은 분명해 보이지만, 이런 글이 나왔다는 사실 자체가 객관적으로 “분명한” 문제는 아니라는 뜻이기도 함 :D- 정말로 0…255.0이 당연하다면, 어떤 부동소수점 값 범위가 정수 0으로 돌아가고 어떤 값이 정수 255로 돌아가야 함?
0..<1이 정수 0으로 가고, 254>..255.0이 정수 255로 간다고 하면 128이 잡아먹힘. 아마 127.5..128.5가 128로 가길 원할 텐데, 그러면 이 절반들은 어디로 가야 함?
128을 맞추려고 전체를 조금 이동시키면 0..0.99609375가 정수 0으로 매핑됨
- 정말로 0…255.0이 당연하다면, 어떤 부동소수점 값 범위가 정수 0으로 돌아가고 어떤 값이 정수 255로 돌아가야 함?
- 표준 접근도 사람들이 자연스럽게
round()를 호출하면서 생긴 것처럼 보임
사람들에게 그 방식이 꽤 자연스럽게 느껴지니, 단순함 때문에 표준이 된 것 같음 - 256으로 달성하려던 것의 반대 방식도 쓸모가 있을지 궁금함. 즉 0.0은 0, 1.0은 255로 보내고, 나머지 부동소수점 값은 1부터 254로 매핑하는 방식임
처리 중에도 검정은 검정으로, 흰색은 흰색으로 유지되면 좋겠음uint8_t output = 0.0f >= result ? 0 : 1.0f <= result ? 255 : 1 + 253*result;- 이렇게 하면 0과 255가 단위 구간에서 다른 숫자보다 더 큰 몫을 가져감. 대략 0.8%, 즉 255/253 정도임
- 첫 번째 이미지가 내 환경에서는 깨져 보임
- 글 작성자임. 이미지 파일이 손상됐다는 뜻인가요?
pngcrush로 압축하긴 했음. 아니면 이미지 내용이 뭔가 잘못됐다는 뜻인가요?
- 글 작성자임. 이미지 파일이 손상됐다는 뜻인가요?