← README · 4절 1 · 2 · 3 · 4 · English
러프 1 · 러프 2는 같은 단계를 거치고, 다른 것은 아래 표의 값과 0.57부터 러프 2가 4 · 6 대신 11(G펜으로 따라 긋기)을 쓰는 것입니다. 전용 난수(mulberry32, 씨앗 러프 1은 20261011, 러프 2는 20261012)를 써서 다른 층과 상관없이 같은 그림이면 같은 러프가 나옵니다.
아래에서 $s' = \max(1, s)$는 선 굵기 · 비낌의 크기 비율, $s_g = \max(0.5, s)$는 흐리기 · 허용 오차 · 길이 문턱의 크기 비율(작은 그림은 같은 모양이 더 적은 픽셀)이고, 𝑢는 쓸 때마다 새로 뽑는 $[0, 1)$의 난수, 𝑣는 $[-1, 1)$의 난수입니다.
풀이 — 왜: 러프는 사람이 그은 듯 흔들려야 하지만, 같은 그림에서는 늘 같은 러프가 나와야 결과를 비교 · 재현할 수 있습니다. 크기 비율은 선 굵기와 모양 문턱이 작은 그림에서 지나치게 가늘어지거나 커지지 않게 나눠 둡니다. 원리: mulberry32는 32비트 상태 하나를 정해진 상수만큼 더하고 섞는 의사 난수 생성기라, 씨앗이 같으면 같은 수열이 나옵니다. 러프 전용 씨앗을 써서 다른 층의 난수 사용과 상관없이 러프가 정해집니다. $s' = \max(1, s)$는 선 굵기를 작은 그림에서도 1배 아래로 줄이지 않고, $s_g = \max(0.5, s)$는 길이 문턱을 작은 그림에서 절반까지만 줄입니다. 𝑢는 균등 난수 $[0, 1)$, $v = 2u - 1$은 $[-1, 1)$입니다. 참고: R48 Ettinger 2017 · 이 프로그램에서 정한 규칙
| 값 | 러프 1(컨셉) | 러프 2(다듬기) | 단계 |
|---|---|---|---|
파츠 흐리기 $\sigma_p$ · 큰 파츠 문턱 $a_{min}$ |
$6s_g$ · 캐릭터 넓이의 0.8% |
$2.5s_g$ · 0.2% |
1 |
| 안쪽 색 경계 · 선화가 있는 실루엣 | 밑에 선화가 있으면 긋기 · 긋기 | 긋지 않음 · 선화가 긋기 | 2 |
| 구도 어긋남 | 회전 $0.05v$ rad, 크기 $1 + 0.04v$, 휨 캐릭터 크기의 3.5% |
없음 | 3 |
| 안쪽 선(선 알파 문턱 · 길이 · 촘촘함) | 0.3 · $40s_g$ 이상(넓게 성긴 곳은 $12s_g$) · 0.11 이하 |
0.25 · $12s_g$ 이상(캐릭터 밖은 $25s_g$) · 0.18 이하(넓게 성긴 곳은 촘촘해도) |
4 |
| 덩어리 선 가까이의 안쪽 선 | 버림 | 긋기 | 4 |
| 배경 구도 | 긋지 않음 | 밝기 경계 $40s_g$ 이상, 세기 0.45 |
5 |
| 살색 · 얼굴 자리 짐작 | a* 4 이상 · 얼굴을 하나도 못 찾은 그림의 한 덩어리 | a* 2 이상, a* + b* 6 이상 · 찾은 얼굴이 없는 덩어리 모두 | 6 |
| 획 맞춤 | 꺾임 0.8 rad에서 끊고 3차식(허용 오차 $3s_g$, 얼굴 $1.2s_g$) |
꺾임 0.9 rad에서 끊고, 모서리(0.65 rad)마다 3차식(허용 오차 $3.5s_g$, 얼굴 $0.7s_g$)으로 맞춘 뒤 1.2 rad 이하로 꺾이는 조각은 한 획으로 잇기 |
7 |
| 한 획 길이 · 겹침 | $(110 + 60u)s_g$ · 18% |
$(90 + 50u)s_g$ · 10% |
7 |
| 덩어리 선 반지름 · 비낌(첫 번 · 덧긋기) · 실루엣 / 안쪽 세기 | $1.2s'$ · $0.8s'$ · $2.2s'$ · 0.9 / 0.7 |
$1.7s'$ · $0.3s'$ · $0.5s'$ · 1 / 0.85 |
8 |
| 안쪽 선 반지름 · 횟수 · 세기 배율 | $1.15s'$ · 2번 · 0.8 |
$1.6s'$ · 1번(50%로 2번) · 1 |
8 |
| 얼굴 획 반지름 · 진하기 · 비낌 | $1.1s'$ · $0.35 + 0.65\bar\ell$ · $0.35s'$, $0.9s'$ |
$1.8s'$ · $0.8 + 0.2\bar\ell$ · $0.12s'$, $0.35s'$ |
8 |
획 끝 필압 바닥 $p_0$ · 짜임선 진하기 |
0.18 · 1.3배 | 0.35 · 0.5배 | 8, 9 |
풀이 — 왜: 러프 1(컨셉)은 큰 덩어리와 구도만 크게 흔들려 잡고, 러프 2(다듬기)는 실제 윤곽에 가깝게 확정한 선이라 두 층의 값이 다릅니다. 원리: 길이 · 반지름은 모두 크기 비율($s_g$, $s'$)의 배수라 그림 크기가 달라도 같은 모양이 나옵니다. 러프 1은 흐리기 · 허용 오차 · 비낌이 크고 획이 길며 겹침이 많고, 러프 2는 그 반대입니다. 참고: 이 프로그램에서 정한 규칙
얼굴 자리 𝐹: 이름 · 폴더용 얼굴(4.5 얼굴 찾기의 9)마다 두 눈 가운데 $(c_x, c_y)$, 눈 사이 거리 𝑑, 눈 방향 단위 벡터 𝐞, 그에 수직인 아래 방향 𝐟로 $u = (\mathbf p - \mathbf c)\cdot\mathbf e / d$, $v = (\mathbf p - \mathbf c)\cdot\mathbf f / d$일 때 $\lvert u\rvert \le 1.1$, $-0.6 \le v \le 1.6$인 픽셀입니다(눈썹부터 턱까지).
풀이 — 왜: 얼굴은 다른 곳과 다르게(곁가지를 치지 않고 더 곱게) 그어야 하므로 얼굴 자리를 정해 둡니다. 원리: 눈 사이 거리 𝑑와 눈 방향 정규 직교 기저 $(\mathbf e, \mathbf f)$에 내적해 얻은 얼굴 틀 좌표 $(u, v)$(4.5)에서 눈썹부터 턱까지의 사각형입니다. 얼굴 크기 · 기울기와 상관없이 같은 자리가 잡힙니다. 참고: R35 Strang 2016
N_c$의 $a_{min}$ 이상이면서 $150s_g^2$픽셀 이상인 무리를 큰 파츠로 둡니다.
배경 · 캐릭터 지시 함수를 $\sigma_p$로 흐려 캐릭터 쪽이 큰 곳을 실루엣 안으로 정하고(실루엣은 캐릭터 전체로만 정함), 그 안은 큰 파츠마다와 “나머지”(작은 조각 · 프릴)의 지시 함수를 흐린 값이 가장 큰 것으로 칠해 지도 $L_2$를 얻습니다.
큰 파츠만으로 칠하면 작은 조각이 많은 곳이 배경처럼 비어 덩어리가 조각나기 때문입니다.
\sigma_p$로 흐리면 각 픽셀 둘레에서 그 파츠가 차지하는 비율이 됩니다. 가장 큰 값을 고르는(argmax) 칠하기는 작은 돌기를 지우고 경계를 매끈하게 하는 다수결 필터입니다. 작은 조각들을 “나머지” 한 무리로 함께 세는 것은, 작은 조각이 많은 곳이 배경처럼 비지 않게 하기 위해서입니다. 큰 파츠 문턱 $150s_g^2$은 넓이라 크기 비율의 제곱을 곱합니다. 참고: R14 Gonzalez · Woods 2018 · 이 프로그램에서 정한 규칙L_2$가 다르고 한쪽이 캐릭터 밖이면 실루엣, 두 큰 파츠의 밑색 $\Delta E \ge 15$이면 안쪽 경계입니다(나머지와 맞닿은 곳, 𝐹 안은 뺌).
아래 7의 방법으로 줄을 따되(조각 $20s_g$픽셀 이상, 곁가지 $5s_g$걸음 치기, $30s_g$픽셀 미만 고리는 버림), 안쪽 경계 줄은 점의 60% 이상이 선화(선 알파 ≥ 0.25)에서 $r_k = \max(2, 2.5s_g)$px 안일 때만 씁니다.
빛 · 그림자로 갈린 같은 머리카락처럼 선이 없는 색 경계는 작가의 러프에 없기 때문입니다. 러프 2는 안쪽 경계를 긋지 않습니다(복잡한 그림에서 세포 그물 같은 다각형 망이 됨).
러프 2는 러프 1의 덩어리를 대고 실제 윤곽을 다시 긋듯, 실루엣 줄 중 선화가 $r_k$ 안에 있는 점에서 줄을 끊어, 선화가 없는 구간($10s_g$ 이상)만 덩어리 선으로 남깁니다. 선화가 있는 곳의 윤곽은 4의 선화 선이 긋습니다.
\Delta E \ge 15$는 분명히 다른 색이라는 문턱이고, 안쪽 경계는 점의 60% 이상이 실제 선화에서 $r_k$ 안일 때만 써서 선이 있는 경계만 남깁니다. 러프 2는 선화가 있는 실루엣 구간을 끊어 선화 선(4)에 맡기고, 선화가 없는 구간만 덩어리 선으로 남깁니다. 참고: R7 CIE 2004 · 이 프로그램에서 정한 규칙\mathbf p' = \mathbf m + z\,R_\theta(\mathbf p - \mathbf m) + 0.035S\,\big(n_x(\mathbf p),\ n_y(\mathbf p)\big)$로 옮겨 긋습니다. $n_x, n_y$는 칸 크기 $\max(8, 0.45S)$인 값 잡음(4.2)입니다.
\mathbf m + zR_\theta(\mathbf p - \mathbf m)$는 무게 중심을 중심으로 한 회전($R_\theta$, 최대 약 2.9°) · 크기 변환(±4%)이고, 더해지는 $0.035S(n_x, n_y)$는 값 잡음(4.2)으로 만든 부드러운 변위장이라 위치마다 조금씩 다르게 휩니다. 칸 크기가 캐릭터 대각선의 45%라 큰 부분 단위로 휩니다. 참고: R35 Strang 2016 · R47 Ebert et al. 20034s_g$px 안인 줄을 버립니다.
촘촘함: 창 반지름 $r_d = \max(2, \mathrm{round}(10s_g))$ 안의 중심선 · 덩어리 선 픽셀 비율 ρ를 줄을 따라 평균해 $\rho\,(2r_d + 1)/21$로 견줍니다. 나란한 선의 간격으로 따지면 크기와 무관한 값입니다.
무늬 · 빗금 · 머리카락 결처럼 촘촘한 곳의 선은 러프에 없으므로 버리되, 4배 넓은 창($4r_d$)의 같은 값이 0.12 이하인 줄(넓게 보면 성긴 곳에 작게 모인 선: 눈 · 입 · 손가락)은 짧거나 촘촘해도 남깁니다.
길이가 캐릭터 밖 문턱(표)보다 짧은 줄은 점의 절반 이상이 실루엣 안일 때만 씁니다(서명 · 배경 글자 빼기).
무늬 고리: 둘레 $60s_g$픽셀 이하의 닫힌 줄마다 가운데와 평균 반지름 𝑟을 재어, 반지름 비 2배 안이고 가운데 거리가 $6\max(r_i, r_j)$ 이하인 닫힌 줄이 둘 이상 더 있으면 되풀이되는 무늬(스웨터 무늬 · 눈송이 · 꽃잎 · 난간 칸 · 창문 줄)로 보고 긋지 않습니다.
1/g$입니다. 여기에 $(2r_d + 1)/21$을 곱해 기준 창(𝑠 = 1일 때 한 변 21px)에 맞추면, 그림이 커져 간격과 창이 같이 커져도 같은 값이 나옵니다. 4배 넓은 창에서 성기면 넓게 보아 외딴 곳에 작게 모인 선이라 남깁니다. 무늬 고리는 반지름이 비슷한 닫힌 줄이 가까이 셋 이상 되풀이되는지로 봅니다. 참고: 이 프로그램에서 정한 규칙1.5s'$로 흐린 뒤 소벨 기울기의 크기 · 방향을 재어 기울기 방향 최대만 남기고,
크기 0.045 이상에서 시작해 0.02 이상인 이웃으로 이어(히스테리시스) 경계를 얻습니다(그은 덩어리 선에서 $3s_g$px 안은 뺌).
이를 7의 방법으로 따, 길이 $40s_g$ 이상(닫힌 줄은 $80s_g$ 이상)이고 4의 촘촘함 · 무늬 고리 거르기를 지나는 줄만 세기 0.45로 한 번 긋습니다(하프톤 · 풀숲 · 구름 결은 빠짐).
1.5s'$는 잡음을 줄이고, 길이 문턱과 촘촘함 거르기로 짧은 결을 뺍니다. 참고: R19 Sobel · Feldman 1968 · R18 Canny 19868s_g$픽셀 미만 고리만 뺍니다.
r = \max(3, 0.035\sqrt{N_c})$로 닫아(거리 변환으로 넓혔다 좁힘) 생긴 덩어리 중,
넓이가 캐릭터의 0.4%(러프 2는 0.2%) 이상이고 살색이 아닌 구멍 속 진한 선(선 알파 ≥ 0.5)이 덩어리 넓이의 1% 이상인 것의 안쪽(가장자리에서 $2s_g$ 넘게)을 씁니다.
얼굴을 하나도 못 찾은 그림은 그중 구멍 속 선이 가장 많은 하나를 얼굴로 보고, 러프 2는 찾은 얼굴이 없는 덩어리(못 찾은 얼굴 · 손)도 모두 씁니다.
\sqrt{N_c}$(캐릭터 넓이의 제곱근, 즉 길이 단위)에 비례해 캐릭터 크기에 맞춰집니다. 구멍 속 진한 선이 덩어리의 1% 이상이어야 팔 · 다리 같은 맨살 덩어리가 빠집니다. 참고: R15 Soille 2003 · R21 Borgefors 1986\tau = \max(1.5,\ 2.5s_g)$ 이상인 두꺼운 속을 덩어리로 묶고, 가장 큰 반지름 $\rho_{max} \ge 2\tau$이며 긴 쪽이 $4\rho_{max} + 4$px 이하로 둥글게 모인 것만 고릅니다(굵은 선 · 선 끝은 빠짐).
속 픽셀마다 그 안쪽 반지름의 원을 모은 모양(구멍은 채움)을 선에서 빼고, 그 바깥 둘레를 닫힌 줄로 따서 얼굴 획으로 긋습니다. 칠한 눈동자를 가는 선으로 줄이면 점 하나가 되기 때문입니다. 가늘게 붙은 속눈썹 · 눈꺼풀 선은 그대로 중심선으로 긋습니다.
4\rho_{max} + 4$ 이하) 둥글게 모인 모양입니다. 속 픽셀마다 그 반지름의 원을 모으면 원래 모양이 돌아오는데, 이것은 중심축 변환(블럼)에서 최대 원들의 합이 모양을 되살리는 성질입니다. 그 모양의 바깥 둘레만 닫힌 줄로 긋습니다. 참고: R37 Blum 1967 · R21 Borgefors 19863s' + 1.5$px 안이고 바깥쪽 방향의 내적이 $-0.766$ 미만(40° 안으로 곧게 이어짐)인 짝은 한 줄로 잇습니다.
-0.766 = \cos 140^\circ$ 미만이면 두 방향이 거의 마주 보아(곧게 이어지는 방향에서 40° 안) 한 줄로 잇습니다. 참고: R36 Zhang · Suen 1984 · R14 Gonzalez · Woods 2018\mathrm{clamp}(\mathrm{round}(n/25), 2, 6)$(러프 2의 덩어리 · 안쪽 선은 1, 얼굴 선은 2)의 이동 평균을 두 번 하고 1.5px 간격으로 다시 고릅니다.
\mathrm{clamp}(\mathrm{round}(n/25), 2, 6)$의 이동 평균을 두 번 하면 삼각형 가중 평균이 되어 계단이 사라집니다. 긴 줄일수록 창이 넓습니다. 1.5px 간격으로 다시 고르면 곡률 · 길이 계산이 점 밀도에 흔들리지 않습니다. 참고: R14 Gonzalez · Woods 2018i - 4 \to i$와 $i \to i + 4$ 방향 사이 각이 문턱을 넘고 앞뒤 점보다 작지 않으며 앞 꺾임에서 4점 이상 떨어진 점에서 끊습니다. 사람은 꺾인 곳에서 연필을 떼고 새로 긋기 때문입니다.
t \in [0, 1]$에 대한 3차식 $x(t), y(t)$로 최소제곱 맞추고, 가장 먼 점까지의 오차가 허용 오차를 넘으면 그 점에서 나눠 다시 맞춥니다(9점 미만은 그대로).
x(t), y(t)$를 각각 3차 다항식으로 최소제곱 맞춥니다(정규방정식). 가장 먼 점의 오차가 허용 오차를 넘으면 그 점에서 둘로 나눠 다시 맞추는 재귀 분할이라, 허용 오차 안에서 가장 적은 조각으로 맞춰집니다. 참고: R38 Schneider 1990 · R43 Björck 199610s_g$ 미만 마디를 낀 꼭짓점은 빼서 생기는 어긋남이 작은 것부터 $2\times$허용 오차까지 뺍니다(작은 톱니 없애기).
남은 꼭짓점 중 0.65 rad 넘게 꺾이는 곳에서 나눠 그 사이를 위의 3차식으로 맞춥니다. 둘레 $60s_g$ 이하의 닫힌 줄(눈 · 단추)은 모서리로 나누지 않고 얼굴 허용 오차로 매끈하게 맞춥니다.
k = \min(3, \lceil \text{길이} / \text{한 획 길이}\rceil)$획으로 나누고 이웃 획을 겹침 비율만큼 겹칩니다(한 곡선을 2~3획으로 그음).
k = \min(3, \lceil \text{길이}/\text{한 획 길이}\rceil)$로 나눌 획 수를 정하고(최대 3), 이웃 획을 겹침 비율만큼 겹쳐 이음매가 끊기지 않게 합니다. 참고: 이 프로그램에서 정한 규칙k = 0, 1, 2$)는 다음과 같습니다.
\ell_e = \min(9s_g, 0.2\,\text{획 길이})$일 때 첫 긋기는 $e_0, e_1 = -0.6u\,\ell_e$, 덧긋기는 $e_0, e_1 = (1.6u - 0.6)\,\ell_e$입니다. 양수면 그만큼 늦게 시작하거나 일찍 멈추고(못 미침), 음수면 그만큼 접선 방향으로 늘립니다(지나침). 얼굴 획은 못 미치지 않습니다.
-0.6u\ell_e$라 늘 조금 지나치고(0~0.6배), 덧긋기는 $(1.6u - 0.6)\ell_e$라 −0.6~1배 사이에서 지나치거나 못 미칩니다. $\ell_e$를 획 길이의 20% 이하로 묶어 짧은 획이 사라지지 않게 합니다. 참고: 이 프로그램에서 정한 규칙t \in [0, 1]$일 때 법선 방향으로 $o(t) = g_0 + g_l(t - 0.5) + g_1\sin(\pi t + \varphi)$만큼 옮깁니다. $g_0 = 0.9\,\sigma_k v$, $g_l = 1.6\,\sigma_k v$, $g_1 = 1.4\,\sigma_k v$, $\varphi = \pi u$이고 $\sigma_k$는 표의 비낌(첫 번 · 덧긋기)입니다.
o(t)$는 상수(평행 이동) + 1차(기울기) + 반 주기 사인(가운데 휨)의 합입니다. 세 진폭이 모두 표의 비낌 $\sigma_k$와 같은 난수 𝑣에 비례하고, 위상 𝜑로 휨 모양이 획마다 다릅니다. 참고: 이 프로그램에서 정한 규칙필압: $\tau(t) = \mathrm{smoothstep}(0, t_a, t)\,\big(1 - \mathrm{smoothstep}(1 - t_b, 1, t)\big)$, $t_a = 0.12 + 0.18u$, $t_b = 0.15 + 0.2u$일 때
$\displaystyle \mathrm{pr}(t) = P_k\,\big(p_0 + (1 - p_0)\,\tau(t)\big)\,\big(0.88 + 0.12\sin(2\pi f t + \psi)\big),\qquad P_0 = 0.75 + 0.3u,\quad P_{k \ge 1} = 0.5 + 0.3u,\quad f = 1 + 2u,\quad \psi = 2\pi u$
$p_0$는 표의 필압 바닥입니다(러프 2는 확정한 선이라 끝을 덜 가늘게 뺌).
\tau(t)$는 오르는 smoothstep과 내리는 smoothstep을 곱한 사다리꼴 꼴이라 양 끝에서 0, 가운데서 1입니다. 필압 바닥 $p_0$가 끝을 완전히 0으로 만들지 않게 하고, $0.88 + 0.12\sin(2\pi ft + \psi)$는 획 하나에 1~3번 오르내리는 ±12%의 떨림입니다. 첫 긋기가 덧긋기보다 셉니다($P_0 > P_{k\ge1}$). 참고: R47 Ebert et al. 2003 · 이 프로그램에서 정한 규칙\lambda = 0.45 + 0.4\,\mathrm{clamp}\big((R/s' - 1.5)/2,\ 0,\ 1\big)$일 때 $r = R\,\big(\lambda + (1.3 - \lambda)\min(1, \mathrm{pr})\big)$입니다.
r = R(\lambda + (1.3 - \lambda)\min(1, \mathrm{pr}))$은 필압 0에서 $\lambda R$, 필압 1에서 $1.3R$인 1차 보간입니다. 끝의 굵기 비율 𝜆는 붓이 굵을수록(𝑅/𝑠′가 클수록) 0.45에서 0.85로 커져, 굵은 붓은 끝이 덜 가늘어집니다. 참고: 이 프로그램에서 정한 규칙a = o_k\,(0.35 + 0.65\,\mathrm{pr})$, $o_k = (1 \text{ 또는 덧긋기 } 0.75) \cdot w$. 세기 𝑤는 덩어리 선이 $0.2 + 0.8\,c^2$(𝑐는 획 둘레 $\mathrm{tol} + 2$px 안의 가장 센 경계 세기: 실루엣 · 안쪽), 안쪽 선이 $0.2 + 0.8\min(1, \bar\ell)\cdot\text{표의 세기 배율}$,
얼굴 획이 표의 값(ℓ̄은 획을 따라 잰 선 알파의 평균), 배경 구도가 표의 세기입니다.
0.35 + 0.65\,\mathrm{pr}$)에 세기 𝑤를 곱합니다. 덩어리 선의 $0.2 + 0.8c^2$은 경계 세기 𝑐를 제곱해 약한 경계를 더 옅게 하고, 안쪽 선은 그 자리 선 알파의 평균 ℓ̄에 비례합니다. 덧긋기는 0.75배라 겹친 곳이 조금씩 진해집니다. 참고: 이 프로그램에서 정한 규칙t^*$)까지 거리를 𝑑라 하면
덮인 정도는 $c = \min\big(1,\ r(t^*) + 0.5 - d\big)$(0 이하면 안 덮임), 값은 $c \cdot a(t^*)$입니다(안티에일리어싱).
한 번의 긋기 안에서는 픽셀마다 가장 큰 값 𝑥를 쓰고, 긋기끼리는 스크린으로 쌓습니다: $\mathrm{acc} \gets 1 - (1 - \mathrm{acc})(1 - x)$.
r(t^*)$로 $c = \min(1, r + 0.5 - d)$는 경계 1px 폭에서 1 → 0으로 떨어지는 덮임 근사라 안티에일리어싱이 됩니다(둥근 끝 선분 = 캡슐). 한 번의 긋기 안에서는 최댓값을 써 이웃 선분이 겹친 곳이 진해지지 않고, 긋기끼리는 스크린 $1 - (1 - \mathrm{acc})(1 - x)$로 쌓아 1을 넘지 않으면서 겹칠수록 진해집니다. 참고: R14 Gonzalez · Woods 2018 · R3 ISO 32000-1 2008(\pm0.6, 0.7)$ · 코 밑 $(0, 0.5)$ · 턱 $(0, 1.1)$ 네 곳 중 셋 이상이 살색(밝기 ≥ 55, a* · b* −2 ~ 35)이거나 점수 ≥ 12.5인 얼굴(없으면 그런 얼굴 중 점수가 가장 높은 하나)에서,
얼굴 가운데가 캐릭터 위일 때만 두개골 원(가운데 $(0, -0.5)$, 반지름 $1.3d$, $2\pi/3 + 0.3$ rad 호 셋), 가운데 세로선($v = -1.9 \sim 1.9$), 눈높이 가로선($u = -1.5 \sim 1.5$)을 옅게(원 0.3, 선 0.25 × 표의 배율) 긋습니다.
1.3d$의 원을 끊긴 호 세 개($2\pi/3 + 0.3$ rad씩)로, 세로 가운데선과 눈높이 가로선을 옅게 긋는 루미스식 짜임입니다. 살색이 네 곳 중 셋 이상이거나 점수가 높은 얼굴에만 그어 가짜 얼굴에는 긋지 않습니다. 참고: R39 Loomis 1956 · 이 프로그램에서 정한 규칙a = \min\big(1,\ \mathrm{acc}\,(0.92 + 0.08\,n)\big)$. 𝑛은 칸 크기 $2.5s'$인 값 잡음(4.2)으로, 2~3px 크기의 부드러운 연필 결입니다. 0.57부터 러프 1(연필)은 칸 $1.1s'$의 잡음 $n_f$를 더해 $a = \min\big(1,\ \mathrm{acc}\cdot\max(0,\ 0.74 + 0.18n + 0.22n_f)\big)$(종이 결), 러프 2(G펜)는 결 없이 $a = \min(1, \mathrm{acc})$입니다.
$\mathrm{acc} < 0.01$인 픽셀은 비우고, 8비트 알파는 $\mathrm{round}(255a)$입니다. 러프 색(기본 #2F6FE0), 불투명도 $115/255 \approx 45\%$, 숨김. 두 층은 [러프] 폴더에 러프 1이 아래, 러프 2가 위로 들어갑니다.
2.5s'$의 값 잡음 𝑛으로 0.92~1배를 곱합니다. 0.01 미만은 비우고 8비트로 반올림합니다. 층 불투명도 $115/255 \approx 45\%$는 밑그림처럼 옅게 보이게 하는 값입니다. 참고: R47 Ebert et al. 2003 · 이 프로그램에서 정한 규칙\le 0.25$)으로 하고 진하기를 필압과 상관없이 둡니다(세기 𝑤는 그대로).
\ell(\mathbf p) - \bar\ell_4(\mathbf p) \ge 0.15$인 픽셀을 모아($\bar\ell_r$은 반지름 𝑟 상자 안의 ℓ 평균), $w_p = \sum \ell \,/\, \lvert \mathrm{thin} \rvert$(잉크 합 ÷ Zhang–Suen 세선화한 중심선 길이)를 펜 굵기로 둡니다(중심선이 50픽셀 이하면 1.25). 펜 배율은 $s_P = \mathrm{clamp}(w_p / 1.25,\ 0.5,\ 3)$입니다.s_g$) 대신 그 그림에서 잰 펜 굵기로 길이 문턱 · 반지름을 맞춥니다. 원리: 선의 넓이를 중심선 길이로 나누면 평균 폭입니다. 둘레보다 진한 픽셀만 모아 칠 · 음영 덩어리가 폭을 부풀리지 않게 합니다. 1.25px는 픽시브 AI 생성 그림에서 잰 펜 굵기의 가운데 값 근처입니다. 참고: R36 Zhang · Suen 1984 · 이 프로그램에서 정한 규칙
- 선 고르기(지역 대비): $r_C = \max(2,\ \mathrm{round}(3s_P))$일 때 $\ell \ge 0.25$이고 $\ell - \bar\ell_{r_C} \ge 0.15$ 또는 $\ell - \bar\ell_{2r_C} \ge 0.15$(두 크기 창, 0.58)인 픽셀을 선으로 봅니다.
선 밖 픽셀의 연결 성분 중 그림 가장자리에 닿지 않고 넓이가 $25s_P^2$ 이하인 것은 선으로 메웁니다(반짝임 · 점). 선 밖까지의 거리 $d_{in}$이 $2.5s_P$ 넘는 꽉 찬 속에서 $2.5s_P - 1$ 안이고 $d_{in} > 1.5$인 픽셀은 선에서 빼, 칠한 덩어리는 둘레만 남깁니다.\max(4, 6s_P)$픽셀 이상, 곁가지 $\max(2, 2s_P)$걸음 치기), 점 열을 ±2점 이동 평균으로 두 번 고르고 1.5px 간격으로 다시 고릅니다.
창 $c_w = \max(3,\ \mathrm{round}(3s_P))$점 앞뒤 방향 사이 각이 1.05 rad를 넘고 이웃보다 크며 앞 꺾임에서 $2c_w$점 이상 떨어진 곳에서만 나눕니다(길이 · 맞춤 오차로는 나누지 않음). 끝이 다른 줄에 2px 안으로 닿는 끝은 갈림길 끝, 아니면 열린 끝입니다.\sigma = \min(3.5s_P,\ L/6)$px(0.58까지; 0.59부터는 12)로 점을 폅니다(끝점 밖은 끝점에 대해 점대칭으로 덧대어 끝점이 제자리에 남음). $4s_P$px보다 짧은 조각은 긋지 않습니다.\tilde h = \max(0.5,\ \mathrm{med}\,h - 0.5)$로 기본 반지름 $r_0 = \mathrm{clamp}(1.25\tilde h + 0.3,\ 1.15s_P,\ 2.2s_P)$를 둡니다. 강조는 ±20점 평균 $\bar h_j$로 $e_j = \mathrm{clamp}(0.7 + 0.3(\bar h_j - 0.5)/\tilde h,\ 0.85,\ 1.2)$입니다.
들어감 $t_i = \min(0.18L,\ 5s_P)$, 빠짐 $t_o = \min(0.35L,\ 14s_P)$일 때 획 앞에서 𝑠만큼 온 점의 필압은$\displaystyle \mathrm{pr}(s) = \big(0.15 + 0.85\,E(s/t_i)\big)\,\big(0.05 + 0.95\,E((L - s)/t_o)\big),\qquad E(t) = \mathrm{smoothstep}(0, 1, t)$
(갈림길 끝은 $0.75 + 0.25E$ · $0.7 + 0.3E$로 조금만 가늘게), 반지름은 $r = r_0\,e_j\,\mathrm{pr}$, 진하기는 0.92로 한결같습니다(0.58까지; 0.59부터는 12). 열린 끝은 들어감 · 빠짐 길이의 0.5배만큼 끝 방향으로 늘립니다.
o(t) = 0.45s_P\,\big(0.65\sin(2\pi c t + \varphi_1) + 0.35\sin(2\pi \cdot 2.3\,c t + \varphi_2)\big)$, $c = \max(0.3,\ L/(45s_P))$만큼 옮깁니다. 획은 오른손잡이처럼 대체로 위 → 아래, 왼쪽 → 오른쪽으로 긋습니다(빠지는 끝이 아래 · 오른쪽).45s_P$px의 사인 두 개를 섞은 매끈한 흔들림이고 진폭은 펜 굵기의 절반쯤입니다. 참고: 이 프로그램에서 정한 규칙\sigma = \min(6s_P,\ L/4)$로 세지고, 흔들림 진폭은 $0.15s_P$로 줄며, 기본 반지름은 $r_0 = \mathrm{clamp}(1.4\tilde h + 0.3,\ 0.9s_P,\ 3.2s_P)$, 굵기 강조는 앞뒤 20점 평균 $\bar h_j$로 $e_j = \mathrm{clamp}(0.7 + 0.3\,\bar h_j/\tilde h,\ 0.75,\ 1.35)$입니다. 꺾임에서 나눈 조각은 길이 $6s_P$ 이상만 긋습니다.
t \in [0, 1]$에서$\displaystyle q(t) = \mathrm{clamp}\Big(0.5 + 0.5\,e^{-((t - c)/w)^2} + 0.12\sin(2\pi f t + \varphi) + 0.15\min(1,\ \theta/0.8),\ 0.3,\ 1\Big)$
𝑐 ∈ [0.3, 0.6] · 𝑤 ∈ [0.3, 0.55] · φ는 획마다 무작위, $f = \max(0.5,\ L/(80s_P))$에 [0.7, 1.3]을 곱한 값, θ는 앞뒤 3점 방향 사이의 꺾임각입니다. 반지름은 $r = r_0\,e_j\,\mathrm{pr}\,(0.5 + 0.5q)$입니다(pr은 11의 들어감 · 빠짐).
c_N = \mathrm{clamp}\big(\mathrm{med}(\ell - \bar\ell_{r_C})/0.3,\ 0,\ 1\big)$, 굵기로 $w_N = \mathrm{clamp}(2\tilde h/w_p - 0.5,\ 0,\ 1)$를 두고, 중요도 $m = \min\big(1,\ 0.5c_N + 0.5w_N + 0.25\,[L > 40s_P] + 0.15\,[\tilde\ell > 0.85]\big)$(𝓁̃: 획 위 선 알파의 중앙값), 기본 진하기 $a_0 = 0.95\,(0.7 + 0.3m)$입니다. 점의 절반 넘게 얼굴 자리 안이면 $a_0 \ge 0.95 \times 0.95$로 둡니다. 점의 진하기는 $a = a_0\,(0.75 + 0.25q)\,(0.65 + 0.35\,\mathrm{pr})$입니다.L > 20s_P$인 획의 1/4쯤(무작위)은 획 위치 [0.12, 0.42]에서 시작하는 길이 [0.3, 0.55] 구간을 법선 방향으로 [0.15, 0.3]$s_P$ 비껴, 반지름 $0.7\,r_0\,(0.3 + 0.7\sin\pi u)$ · 진하기 $0.5\,a_0\,(0.3 + 0.7\sin\pi u)$(𝑢: 구간 안 상대 위치)로 한 번 더 긋습니다.8s_P$ 미만(양 끝이 모두 다른 선에 닿지 않으면 $12s_P$ 미만)인 열린 줄은 긋지 않습니다. 0.59e부터는 그 줄의 점 절반 넘게 다른 선이 $2.5s_P$ 안에 있을 때만(나란히 붙은 사다리 가로선 · 잔가시) 지우고, 외톨이 짧은 획(안경테 · 손가락)은 남깁니다.c_x \pm 1.0d$, 세로 $c_y - 0.35d$ ~ $c_y + 1.25d$인 상자(짐작한 얼굴은 짐작 얼굴 자리 전체) 안에서는 11의 지역 대비와 상관없이 $\ell \ge 0.15$를 선으로 삼고, 꽉 찬 덩어리 속을 비우지 않습니다(가운데 한 획).
| 부분 | 내용 |
|---|---|
| 머리 | 8BPS, 버전 1, 예약 6바이트, 채널 3(투명 그림은 4), 높이, 너비, 8비트, 색 모드 3(RGB), 색 모드 데이터 길이 0 |
| 이미지 리소스 | 해상도 정보(8BIM, 0x03ED, 길이 16): 가로 · 세로 72dpi를 16.16 고정소수점 $72 \cdot 2^{16}$으로, 단위 1(인치) · 1 |
| 층 정보 | 층 개수(투명 그림은 음수: 병합 이미지의 첫 알파가 투명도), 층마다 경계(위, 왼쪽, $\text{아래} = \text{위} + \text{높이}$, $\text{오른쪽} = \text{왼쪽} + \text{너비}$) · 채널 4개(번호 −1은 알파, 0 · 1 · 2는 R · G · B, 채널마다 데이터 길이) · 8BIM · 혼합 키 · 불투명도(0~255) · 클리핑(0 / 1) · 플래그(숨김 2, 폴더 레코드는 0x18을 더함) · 0 |
| 층 추가 정보 | 마스크 길이 0, 혼합 범위(길이 40: 회색 + 4채널의 원본 · 대상 범위 0x0000FFFF 10개), 파스칼 이름, luni(유니코드 이름), 폴더는 lsct |
| 채널 데이터 | 층마다 알파, R, G, B 순서로 줄마다 PackBits RLE(압축 방식 1 + 줄 길이 표 + 데이터) |
| 병합 이미지 | 미리보기와 같은 합성 결과(불투명 그림은 흰 바탕 위), 채널 R, G, B(, A) 모두 RLE |
풀이 — 왜: 포토샵 · 다른 그림 프로그램이 읽을 수 있도록 PSD 규격의 바이트 배치를 그대로 따릅니다. 원리: 해상도는 16.16 고정소수점(위 16비트가 정수부)이라 72dpi는 $72 \cdot 2^{16}$입니다. 층 경계는 위 · 왼쪽 · 아래 · 오른쪽 좌표라 아래 = 위 + 높이입니다. 투명 그림은 층 개수를 음수로 적어 병합 이미지의 첫 알파 채널이 투명도임을 알립니다. 참고: R4 Adobe PSD
[길이 1바이트][글자]를 4바이트 배수가 되도록 0으로 채웁니다.
luni는 길이 $4 + 2n$(𝑛 = UTF-16 글자 수)을 4의 배수로 올린 뒤 [n][UTF-16BE 글자][채움], lsct는 끝 표시면 길이 4(3), 폴더면 길이 12(1 열린 폴더 / 2 닫힌 폴더, 8BIM, 혼합 키)입니다.
층 정보 전체 길이도 4의 배수로 채우고, 전체 마스크 정보 길이는 0입니다.
luni는 글자 수 𝑛(4바이트) + UTF-16 글자당 2바이트라 $4 + 2n$바이트이고, 이것을 4의 배수로 올려 0으로 채웁니다. 파스칼 이름도 길이 1바이트 + 글자를 4의 배수로 채웁니다. 참고: R4 Adobe PSD[257 − n][값], 아니면 다음에 같은 값 3개가 시작되기 전까지(최대 128) [n − 1][값 n개]로 씁니다.
한 줄이 가장 길어질 때의 크기는 $w + \lceil w/128\rceil + 2$바이트로 잡아 둡니다. 비어 있는 층(폴더 레코드)은 채널마다 압축 방식 2바이트만 씁니다.
-(n - 1)$, 곧 $257 - n$)이나 “다음 𝑛바이트 그대로”($n - 1$)를 나타냅니다(𝑛 ≤ 128). 가장 나쁜 경우는 모두 그대로 쓰는 경우라 128바이트마다 머리 1바이트가 붙어 $w + \lceil w/128\rceil$바이트에 여유 2바이트를 둡니다. 참고: R5 Apple 1996</Layer group> 끝 표시(lsct 3)를 폴더 내용보다 먼저, 폴더 레코드를 내용 뒤에 넣습니다.norm, mul , scrn, pass를 씁니다.PSD.rename)입력에 PSD · PSB(확장자 .psd · .psb, 또는 형식 image/vnd.adobe.photoshop)가 들어오면 그림을 나누지 않고 이것을 합니다.
예전 이름 → 지금 이름 목록(색트레스 → 색 트레이스)마다 아래처럼 바이트 단위로 읽어 이름이 든 층 레코드만 다시 쓰고,
바뀐 층이 있으면 같은 파일 이름으로 저장합니다(여러 개면 0.4초 간격). 아래에서 from은 예전 이름, to는 지금 이름입니다.
Lr16 · Lr32 · Layr(16 · 32비트 그림의 층 정보)를 같은 방법으로 읽습니다.
층마다 채널 길이를 더해 채널 데이터 전체 길이를 구하고, 추가 정보의 luni 이름 안에 든 from을 모두 to로 바꿉니다.
파스칼 이름도 EUC-KR로 읽어 from이 들어 있으면 바꾼 이름을 EUC-KR로 적습니다(255바이트 이하일 때. 한국어판 포토샵이 다시 저장한 파일).
EUC-KR 표는 TextDecoder('euc-kr')로 0xA1A1~0xFEFE 두 바이트를 모두 풀어 거꾸로 만듭니다.8BIM · 8B64 서명에 맞는 조합을 고릅니다(PSB에서 문서의 8바이트 키와 cinf는 8을 먼저, 나머지는 4를 먼저 시험).luni는 길이 $4 + 2n$을 4의 배수로 올려 다시 쓰고, 그 층의 추가 정보 길이, 층 정보 길이(원래 4의 배수였으면 4, 아니면 2의 배수로 채움. Lr16 등은 4의 배수),
층과 마스크 정보 길이만 새로 적습니다. 채널 픽셀 · 병합 이미지 · 이미지 리소스 · 전역 마스크는 바이트 그대로 옮깁니다. 바꿀 층이 없으면 저장하지 않고 그렇다고 알립니다.
luni 길이 $4 + 2n$을 4의 배수로 올리고, 그 차이만큼 바깥 블록(층 추가 정보 → 층 정보 → 층과 마스크 정보)의 길이를 차례로 고칩니다. 픽셀 데이터는 그대로 옮기므로 그림은 바뀌지 않습니다. 참고: R4 Adobe PSD캔버스 2D의 globalCompositeOperation(multiply, screen)과 $\texttt{globalAlpha} = \text{불투명도}/255$로 겹칩니다.
클리핑 묶음은 포토샵처럼, 기본 층을 불투명하게 만든(알파를 모두 255로) 위에 클리핑 층들을 섞은 뒤 기본 층의 알파로 한 번만 잘라(destination-in) 기본 층의 혼합 모드 · 불투명도로 그립니다.
기본 층이 숨겨지면 클리핑 층도 숨습니다. 폴더는 불투명도가 255 미만이거나 혼합 모드가 통과 · 표준이 아니면 따로 그린 뒤 한 장으로 겹치고, 아니면 안의 층을 그대로 겹칩니다.
PNG · PSD 병합 이미지는 불투명 그림이면 흰 바탕 위에 합성합니다.
풀이 — 왜: 화면 미리보기가 포토샵에서 연 결과와 같아야 PSD를 열기 전에 확인할 수 있습니다. 원리: 브라우저 캔버스의 multiply · screen은 PDF/포토샵과 같은 혼합식이고, 층 불투명도 0~255는 $\texttt{globalAlpha}$ 0~1로 나눠 씁니다. 클리핑은 기본 층의 알파로 한 번만 잘라(destination-in) 포토샵처럼 클리핑 묶음 전체가 기본 층 모양에 갇히게 합니다. 참고: R3 ISO 32000-1 2008 · R4 Adobe PSD
\min(255, 8\lvert \text{합성} - \text{원본} \rvert)$(8비트)로 8배 확대한 차이를 보여 줍니다.
z = \min((W_{view} - 32)/W, (H_{view} - 32)/H, 4)$, 가운데 정렬 $t = (W_{view} - Wz)/2$(세로도 같게).z' = \mathrm{clamp}(z\,e^{-0.0015\Delta},\ 0.05,\ 32)$(Δ = 휠 이동량), 커서 𝑐가 가리키는 점을 고정하도록 $t' = c - (c - t) \cdot z'/z$. 배율 2 이상에서는 픽셀을 그대로 키웁니다.
z' = z\,e^{-0.0015\Delta}$는 지수라 같은 휠 이동이 같은 배율 비(로그 눈금)로 바뀝니다. 커서 아래 그림 점 $(c - t)/z$가 확대 뒤에도 같도록 $t' = c - (c - t)z'/z$로 옮깁니다. 참고: 이 프로그램에서 정한 규칙(x_s, y_s)$의 그림 픽셀은 $(\lfloor(x_s - \text{왼쪽})/z \rfloor, \lfloor(y_s - \text{위})/z \rfloor)$이고, 보정 획은 이전 점과의 거리를 $\lceil \text{거리}/3 \rceil$등분한 점(반올림)을 더해 3px 간격으로 촘촘히 기록합니다.44 / \max(W, H)$배), 불투명도 슬라이더는 $\mathrm{round}(\text{불투명도}/2.55)$%입니다.
\lceil \text{거리}/3\rceil$등분 점을 더해 기록 간격을 3px 이하로 맞춥니다. 미리보기 그림은 긴 변 44px로 줄이고, 불투명도 0~255를 0~100%로 $/2.55$해 보여 줍니다. 참고: 이 프로그램에서 정한 규칙배경으로 보내기
선 알파는 모든 선화 층의 8비트 알파 중 가장 큰 값 $\alpha_8$(0~255)입니다. “선 위”는 $\alpha_8 \ge 110$(약 0.43), “둘레 선”은 $\alpha_8 \ge 20$(약 0.08)입니다.
붓 폭 $R_b = \max(24, \mathrm{round}(0.05 \cdot \max(W, H)))$이고, 획에서의 거리는 획의 점들에서 출발한 4-이웃 너비 우선 걸음 수입니다($R_b$까지).
획의 모든 점이 첫 점에서 $\lvert \Delta x\rvert + \lvert \Delta y\rvert \le 4$ 안이면 한 번 누르기, 아니면 칠하기입니다. 칠하기의 길이는 이웃한 점 사이 거리의 합이고,
48px 이상이면 양 끝 12px(획을 따라 잰 거리)의 점은 획 끝으로 봅니다.
풀이 — 왜: 사용자의 붓질이 선을 넘거나 경계를 스쳐도 의도한 물건만 옮겨야 하므로, 선 · 붓 폭 · 누르기와 칠하기를 먼저 정합니다. 원리: 선화 층들의 알파 중 최댓값을 선 알파로 두고, 110/255(약 0.43) 이상은 넘을 수 없는 선, 20/255 이상은 둘레 선입니다. 붓 폭은 긴 변의 5%(최소 24px)입니다. 모든 점이 첫 점에서 맨해튼 거리 4 안이면 손이 떨린 한 번 누르기입니다. 긴 획의 양 끝 12px는 시작 · 멈춤이 부정확해 채우기 출발점에서 뺍니다. 참고: 이 프로그램에서 정한 규칙
배경으로 보내기
\Delta E < 12 \land \Delta ab < 9$인 파츠입니다(밝기 차이는 넉넉히, 색상 차이는 좁게).
파츠의 색은 밑색 층에서 처음 만나는 불투명 픽셀의 Lab, $\Delta ab = \sqrt{\Delta a^{*2} + \Delta b^{*2}}$입니다.
\Delta ab = \sqrt{\Delta a^{*2} + \Delta b^{*2}}$는 밝기를 뺀 색도 평면의 거리입니다. 전체 $\Delta E < 12$로 밝기 차이는 넉넉히 받고, $\Delta ab < 9$로 색상 · 채도 차이는 좁게 받아 명암 관계인 파츠만 허용합니다. 참고: R7 CIE 2004\max_c \lvert \Delta O_c\rvert \le 40$(파츠가 하나면 26, 8비트)인 4-이웃으로 넓힙니다.
\max_c \lvert \Delta O_c\rvert < 16$) 이어지는 가장자리는 캐릭터 쪽에 남깁니다.
캐릭터로 되돌리기: 배경 안에서 같은 방식(허용치 40, 파츠 구분 없음)으로 고르고, 붓 폭 안에서 배경 쪽으로 2걸음(안티에일리어싱 가장자리)을 더한 뒤 “캐릭터” 마스크에 기록하고 다시 분리합니다.
올가미: 점이 30개 이상, 획 길이 120px 이상이고 끝점이 시작점에서 $\max(24, 0.2 \times \text{외곽 사각형 대각선})$ 안이면 닫힌 고리로 봅니다.
행마다 $y + 0.5$ 높이에서 고리의 변과 만나는 𝑥($x = x_a + (y + 0.5 - y_a)(x_b - x_a)/(y_b - y_a)$, 두 끝점이 그 높이의 서로 다른 쪽에 있는 변)를 정렬해
짝을 이룬 구간 $[\lceil x_1 - 0.5\rceil, \lfloor x_2 - 0.5\rfloor]$을 채웁니다(짝홀 규칙). 안쪽이 400px 미만이면 올가미로 보지 않습니다.
안쪽을 선으로 둘러싸인 조각(배경으로 보내기는 같은 파츠의 4-연결 조각, 캐릭터로 되돌리기는 배경의 4-연결 조각) 단위로 맞춰,
조각 픽셀 중 고리 안의 비율 𝑓가 $f \ge 0.6$이면 통째로, $f \le 0.4$면 제외, 그 사이는 고리 안쪽만 고릅니다.
배경으로 보내기는 고리에서 12걸음 안까지를, 캐릭터로 되돌리기는 고른 곳을 2걸음 넓히되 고리에서 2걸음 안까지를 씁니다.
풀이 — 왜: 물건을 둘레로 감싸 그리면(올가미) 그 안쪽을 한꺼번에 고를 수 있어야 합니다. 원리: 각 행의 픽셀 중심 높이 $y + 0.5$에서 고리의 변과 만나는 𝑥를 1차 보간 $x = x_a + (y + 0.5 - y_a)(x_b - x_a)/(y_b - y_a)$로 구하고, 정렬해 짝지은 구간을 채우는 짝홀 규칙의 주사선 채우기입니다. 두 끝점이 그 높이의 서로 다른 쪽에 있는 변만 세서 꼭짓점을 두 번 세지 않습니다. 선으로 둘러싸인 조각은 고리 안 비율 0.6 이상이면 통째로, 0.4 이하면 빼고 그 사이만 고리를 따릅니다(두 문턱 사이를 비워 고리가 조금 어긋나도 조각 단위로 맞음). 참고: 이 프로그램에서 정한 규칙
다시 나누기 캐시: 자동 배경 판정 결과는 $(W, H, \text{그림 해시}, \text{선 굵기}, \text{선 감도}, \text{선 색 방식}, \text{배경 분리})$가 같으면 다시 씁니다.
그림 해시는 RGBA 바이트 $v_i$에 대한 두 갈래 FNV-1a(32비트 곱셈)와 길이입니다.
h_1\leftarrow (h_1\oplus v_i)\cdot 16777619,\quad h_1^{(0)}=\mathtt{0x811c9dc5};\qquad
h_2\leftarrow (h_2\oplus v_i)\cdot 2246822519+i,\quad h_2^{(0)}=\mathtt{0x9747b28c}\qquad(\bmod 2^{32})
풀이 — 왜: 배경 판정은 오래 걸리므로, 같은 그림 · 같은 설정으로 다시 나눌 때는 앞 결과를 다시 씁니다. 그림이 같은지 빠르게 확인할 지문이 필요합니다. 원리: FNV-1a는 바이트마다 XOR한 뒤 소수 16777619를 곱하는($\bmod 2^{32}$) 빠른 해시입니다. 서로 다른 곱수 · 씨앗과 위치 𝑖를 섞은 둘째 해시와 길이를 함께 써서, 서로 다른 그림이 우연히 같은 지문을 가질 확률을 대략 $2^{-64}$로 낮춥니다. 참고: R40 Fowler et al.