Illustracy

Illustracy — 4. 알고리즘과 수식 (4/4): 러프 · PSD · 미리보기 · 수동 보정

← README · 4절 1 · 2 · 3 · 4 · English


4.14 러프 생성

러프 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

  1. 덩어리 지도: 캐릭터 픽셀(알파 > 0.02이고 배경 톤이 아님)을 밑색 무리로 나누고, 캐릭터 넓이 $N_c$의 $a_{min}$ 이상이면서 $150s_g^2$픽셀 이상인 무리를 큰 파츠로 둡니다. 배경 · 캐릭터 지시 함수를 $\sigma_p$로 흐려 캐릭터 쪽이 큰 곳을 실루엣 안으로 정하고(실루엣은 캐릭터 전체로만 정함), 그 안은 큰 파츠마다와 “나머지”(작은 조각 · 프릴)의 지시 함수를 흐린 값이 가장 큰 것으로 칠해 지도 $L_2$를 얻습니다. 큰 파츠만으로 칠하면 작은 조각이 많은 곳이 배경처럼 비어 덩어리가 조각나기 때문입니다.
    • 풀이 — 왜: 작가의 러프는 세부가 아닌 큰 덩어리(머리, 몸, 옷의 큰 면)를 먼저 그립니다. 작은 조각을 지우고 큰 파츠만 부드럽게 남긴 지도가 필요합니다. 원리: 파츠마다의 0/1 지시 함수를 가우시안 $\sigma_p$로 흐리면 각 픽셀 둘레에서 그 파츠가 차지하는 비율이 됩니다. 가장 큰 값을 고르는(argmax) 칠하기는 작은 돌기를 지우고 경계를 매끈하게 하는 다수결 필터입니다. 작은 조각들을 “나머지” 한 무리로 함께 세는 것은, 작은 조각이 많은 곳이 배경처럼 비지 않게 하기 위해서입니다. 큰 파츠 문턱 $150s_g^2$은 넓이라 크기 비율의 제곱을 곱합니다. 참고: R14 Gonzalez · Woods 2018 · 이 프로그램에서 정한 규칙
  2. 덩어리 선: 픽셀과 오른쪽 · 아래 이웃의 $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 · 이 프로그램에서 정한 규칙
  3. 구도 어긋남(러프 1): 캐릭터 무게 중심 𝐦, 캐릭터 경계 상자의 대각선 𝑆일 때 모든 획의 점을 $\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. 2003
  4. 안쪽 선: 𝐹 밖에서 선 알파가 문턱 이상인 픽셀의 중심선을 땁니다. 러프 1은 점의 절반 넘게 그은 덩어리 선에서 $4s_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)$ 이하인 닫힌 줄이 둘 이상 더 있으면 되풀이되는 무늬(스웨터 무늬 · 눈송이 · 꽃잎 · 난간 칸 · 창문 줄)로 보고 긋지 않습니다.
    • 풀이 — 왜: 러프에는 큰 선만 있고 무늬 · 빗금 · 머리카락 결처럼 촘촘한 선은 없습니다. 짧고 촘촘한 선을 거르되, 눈 · 입 · 손가락처럼 작게 모인 중요한 선은 남겨야 합니다. 원리: 창 안 선 픽셀 비율 ρ는 간격 𝑔로 나란한 1px 선이면 약 $1/g$입니다. 여기에 $(2r_d + 1)/21$을 곱해 기준 창(𝑠 = 1일 때 한 변 21px)에 맞추면, 그림이 커져 간격과 창이 같이 커져도 같은 값이 나옵니다. 4배 넓은 창에서 성기면 넓게 보아 외딴 곳에 작게 모인 선이라 남깁니다. 무늬 고리는 반지름이 비슷한 닫힌 줄이 가까이 셋 이상 되풀이되는지로 봅니다. 참고: 이 프로그램에서 정한 규칙
  5. 배경 구도(러프 2): 배경 그림의 선은 배경 층으로 옮겨졌으므로, 배경 톤 픽셀의 밝기(0.299R + 0.587G + 0.114B)를 $1.5s'$로 흐린 뒤 소벨 기울기의 크기 · 방향을 재어 기울기 방향 최대만 남기고, 크기 0.045 이상에서 시작해 0.02 이상인 이웃으로 이어(히스테리시스) 경계를 얻습니다(그은 덩어리 선에서 $3s_g$px 안은 뺌). 이를 7의 방법으로 따, 길이 $40s_g$ 이상(닫힌 줄은 $80s_g$ 이상)이고 4의 촘촘함 · 무늬 고리 거르기를 지나는 줄만 세기 0.45로 한 번 긋습니다(하프톤 · 풀숲 · 구름 결은 빠짐).
    • 풀이 — 왜: 러프 2는 배경도 구도선을 잡아야 합니다. 배경 그림의 큰 밝기 경계(지평선, 벽 모서리)만 남기고 결 · 하프톤은 빼야 합니다. 원리: 소벨 3×3 연산자로 밝기 기울기의 크기 · 방향을 잰 뒤, 기울기 방향으로 이웃보다 큰 점만 남기고(비최대 억제) 0.045에서 시작해 0.02 이상으로 잇는(히스테리시스) 것은 캐니 경계 검출과 같은 순서입니다. 흐림 $1.5s'$는 잡음을 줄이고, 길이 문턱과 촘촘함 거르기로 짧은 결을 뺍니다. 참고: R19 Sobel · Feldman 1968 · R18 Canny 1986
  6. 얼굴 세부: 𝐹와 짐작한 얼굴 자리 안에서 선 알파 ≥ 0.25인 픽셀의 중심선을 곁가지 치기 없이 4픽셀 조각까지 따고, $8s_g$픽셀 미만 고리만 뺍니다.
    • 얼굴 자리 짐작: 살색 무리(밝기 ≥ 55, a* 문턱 ~ 30, b* 0 ~ 35)를 반지름 $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 1986
  7. 줄과 획: 재료를 3×3으로 1px 넓혀 틈을 잇고 **Zhang–Suen 세선화**로 1px 중심선을 얻은 뒤, 끝점을 정한 걸음만큼 지웠다 되살려 짧은 곁가지를 칩니다(𝐹 안은 치지 않음). 끝점(8-이웃 1개 이하) · 갈림점(8-이웃을 한 바퀴 돌 때 0 → 1 바뀜이 3번 이상)을 마디로 두고 마디 사이 사슬을 따며, 끝끼리 $3s' + 1.5$px 안이고 바깥쪽 방향의 내적이 $-0.766$ 미만(40° 안으로 곧게 이어짐)인 짝은 한 줄로 잇습니다.
    • 풀이 — 왜: 선화 픽셀 덩어리를 붓으로 그을 수 있는 줄(점의 열)로 바꿔야 획을 만들 수 있습니다. 원리: 3×3 팽창으로 1px 틈을 이은 뒤 Zhang–Suen 세선화로 연결을 지키며 1px 중심선을 얻습니다. 끝점을 정해진 걸음만큼 지웠다 되살리면 짧은 곁가지만 사라집니다. 8-이웃을 한 바퀴 돌며 0 → 1 바뀜 수(교차수)가 3 이상이면 갈림점입니다. 두 끝의 바깥쪽 방향 내적이 $-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 2018
    • 꺾이는 곳: 점 $i - 4 \to i$와 $i \to i + 4$ 방향 사이 각이 문턱을 넘고 앞뒤 점보다 작지 않으며 앞 꺾임에서 4점 이상 떨어진 점에서 끊습니다. 사람은 꺾인 곳에서 연필을 떼고 새로 긋기 때문입니다.
      • 풀이 — 왜: 사람은 꺾인 곳에서 연필을 떼고 새로 긋기 때문에, 꺾이는 곳에서 획을 끊어야 그은 느낌이 납니다. 원리: 점 𝑖 앞뒤 4점 방향 벡터 사이 각(꺾임각)이 문턱을 넘고 이웃보다 크며(국소 최대), 앞 꺾임에서 4점 이상 떨어진 곳만 꺾임으로 봅니다. 참고: 이 프로그램에서 정한 규칙
    • 완만한 곡선: 꺾임 사이를 호 길이 $t \in [0, 1]$에 대한 3차식 $x(t), y(t)$로 최소제곱 맞추고, 가장 먼 점까지의 오차가 허용 오차를 넘으면 그 점에서 나눠 다시 맞춥니다(9점 미만은 그대로).
      • 풀이 — 왜: 꺾임 사이의 줄을 붓 획처럼 매끈한 곡선으로 바꿉니다. 원리: 호 길이를 0~1로 맞춘 매개변수 𝑡에 대해 $x(t), y(t)$를 각각 3차 다항식으로 최소제곱 맞춥니다(정규방정식). 가장 먼 점의 오차가 허용 오차를 넘으면 그 점에서 둘로 나눠 다시 맞추는 재귀 분할이라, 허용 오차 안에서 가장 적은 조각으로 맞춰집니다. 참고: R38 Schneider 1990 · R43 Björck 1996
    • 러프 2의 모서리: 꺾임 사이를 먼저 가장 먼 점에서 나누어 허용 오차 안의 곧은 마디로 줄이고(Ramer–Douglas–Peucker), 길이 $10s_g$ 미만 마디를 낀 꼭짓점은 빼서 생기는 어긋남이 작은 것부터 $2\times$허용 오차까지 뺍니다(작은 톱니 없애기). 남은 꼭짓점 중 0.65 rad 넘게 꺾이는 곳에서 나눠 그 사이를 위의 3차식으로 맞춥니다. 둘레 $60s_g$ 이하의 닫힌 줄(눈 · 단추)은 모서리로 나누지 않고 얼굴 허용 오차로 매끈하게 맞춥니다.
      • 풀이 — 왜: 러프 2는 확정한 선이라 모서리는 또렷이 꺾고 그 사이는 매끈해야 하며, 픽셀 잡음에서 생긴 작은 톱니는 없어야 합니다. 원리: Ramer–Douglas–Peucker는 양 끝을 잇는 직선에서 가장 먼 점에서 나누기를 허용 오차 안이 될 때까지 되풀이해 줄을 적은 꼭짓점의 꺾은선으로 줄입니다. 짧은 마디를 낀 꼭짓점을 어긋남이 작은 것부터 빼서 톱니를 없애고, 0.65 rad 넘게 꺾이는 꼭짓점에서만 나눠 그 사이를 3차식으로 맞춥니다. 참고: R41 Ramer 1972 · R42 Douglas · Peucker 1973 · R38 Schneider 1990
    • 이어 긋기(러프 2): 맞춘 조각들 가운데 끝점이 맞닿고 그 사이 꺾임이 1.2 rad 이하인 것은 붓을 떼지 않고 한 획으로 잇습니다. 모서리는 한 획 안에서 꺾고, 날카로운 꺾임에서만 새로 긋습니다.
    • 한 획 길이보다 긴 곡선은 $k = \min(3, \lceil \text{길이} / \text{한 획 길이}\rceil)$획으로 나누고 이웃 획을 겹침 비율만큼 겹칩니다(한 곡선을 2~3획으로 그음).
      • 풀이 — 왜: 사람은 긴 곡선을 한 번에 긋지 않고 2~3획으로 나눠 이어 긋습니다. 원리: $k = \min(3, \lceil \text{길이}/\text{한 획 길이}\rceil)$로 나눌 획 수를 정하고(최대 3), 이웃 획을 겹침 비율만큼 겹쳐 이음매가 끊기지 않게 합니다. 참고: 이 프로그램에서 정한 규칙
  8. 긋기: 획마다 정해진 횟수를 긋습니다(덩어리 선 2번(30%로 3번), 얼굴 획 2번(45%로 3번), 짜임선 1~2번). 𝑘번째 긋기($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{표의 세기 배율}$, 얼굴 획이 표의 값(ℓ̄은 획을 따라 잰 선 알파의 평균), 배경 구도가 표의 세기입니다.
      • 풀이 — 왜: 뚜렷한 경계 · 진한 선 자리의 획은 진하게, 약한 곳은 옅게 그어 러프에서도 중요한 선이 먼저 보이게 합니다. 원리: 진하기는 필압의 1차식($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
  9. 얼굴 짜임: 캐릭터 얼굴(4.8의 캐릭터 나누기와 같은 고르기) 중 두 뺨 $(\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 · 이 프로그램에서 정한 규칙
  10. 알파: $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~3px의 얼룩덜룩한 결이 있습니다. 원리: 쌓인 값 acc에 칸 크기 $2.5s'$의 값 잡음 𝑛으로 0.92~1배를 곱합니다. 0.01 미만은 비우고 8비트로 반올림합니다. 층 불투명도 $115/255 \approx 45\%$는 밑그림처럼 옅게 보이게 하는 값입니다. 참고: R47 Ebert et al. 2003 · 이 프로그램에서 정한 규칙
  11. G펜으로 따라 긋기(러프 2, 0.57부터): 러프 2는 4(안쪽 선)와 6(얼굴 세부)를 긋지 않고, 아래처럼 선화를 G펜으로 한 번씩 따라 긋습니다. 1 · 2 · 5 · 9(덩어리 · 실루엣 · 배경 구도 · 얼굴 짜임)는 그대로 긋되, 8의 긋기를 한 번(덧긋기 없음, 비낌 $\le 0.25$)으로 하고 진하기를 필압과 상관없이 둡니다(세기 𝑤는 그대로).
    • 펜 굵기: 선 알파 ℓ ≥ 0.25이고 캐릭터 안(알파 ≥ 0.5)이며 $\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$인 픽셀은 선에서 빼, 칠한 덩어리는 둘레만 남깁니다.
    • 풀이 — 왜: 선 알파만으로 고르면 칠 · 음영 · 머리카락 결처럼 넓게 어두운 곳이 선으로 들어오고, 그 덩어리를 세선화한 가운데 줄이 의미 없는 그물이 됩니다(0.56까지 처음 보는 그림의 약 17%). 선은 둘레보다 진한 가는 띠이므로 둘레 평균과의 차이로 고릅니다. 굵은 선(눈 테두리 · 속눈썹, 5~6px)은 가운데가 작은 창의 평균과 비슷해 두 가장자리만 남으므로(작은 눈이 네모 상자처럼 됨) 두 배 창을 함께 봅니다. 원리: 상자 평균은 적분 영상으로 픽셀마다 상수 시간에 구합니다. 작은 구멍 메우기는 4-이웃 채우기로 성분 넓이를 재고, 꽉 찬 덩어리는 거리 변환(체임퍼)으로 속을 찾아 그 둘레 띠만 남깁니다. 참고: 이 프로그램에서 정한 규칙 - 줄과 조각: 7의 방법으로 줄을 따되(조각 $\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 안으로 닿는 끝은 갈림길 끝, 아니면 열린 끝입니다.
    • 풀이 — 왜: 프로 작가는 완만한 곡선을 한 획으로 긋고, 정말 꺾이는 곳(약 60° 넘게)에서만 펜을 뗍니다. 원리: 꺾임각의 국소 최대만 자르는 점은 7과 같고, 문턱을 크게 하고 길이 · 3차식 오차로 나누지 않아 획 수가 줄어듭니다. 참고: 이 프로그램에서 정한 규칙 - 손떨림 보정: 조각마다 호 길이에 대한 가우스 $\sigma = \min(3.5s_P,\ L/6)$px(0.58까지; 0.59부터는 12)로 점을 폅니다(끝점 밖은 끝점에 대해 점대칭으로 덧대어 끝점이 제자리에 남음). $4s_P$px보다 짧은 조각은 긋지 않습니다.
    • 풀이 — 왜: 사람은 그림 프로그램의 손떨림 보정을 켜고 긋기 때문에 획에 잔톱니가 없습니다. 원리: 가우스 가중 평균은 짧은 파장의 흔들림만 지우는 저역 통과 필터입니다. 점대칭 덧대기는 끝에서 곡선이 안쪽으로 줄어드는 것을 막습니다. 참고: 이 프로그램에서 정한 규칙 - 필압 · 굵기: 획의 점마다 선 밖까지의 거리(3×3 안 최대) ℎ, 그 중앙값 $\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배만큼 끝 방향으로 늘립니다.

    • 풀이 — 왜: G펜은 잉크 진하기는 같고 누르는 힘에 따라 굵기만 바뀝니다. 펜선은 들어갈 때 짧게, 빠질 때 길게 가늘어지고, 작가가 굵게 그은 곳은 더 눌러 그은 것입니다. 원리: 선 밖까지의 거리는 그 자리 선 반폭 + 0.5쯤이라 중앙값으로 원래 선의 굵기를 얻습니다. 두 smoothstep의 곱은 양 끝에서만 0으로 내려가는 필압이고, 늘린 끝은 원래 선 끝도 가늘게 빠지므로 끝까지 닿게 합니다. 참고: 이 프로그램에서 정한 규칙 - 흔들림 · 방향: 법선 방향으로 $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의 사인 두 개를 섞은 매끈한 흔들림이고 진폭은 펜 굵기의 절반쯤입니다. 참고: 이 프로그램에서 정한 규칙
  12. 필압 곡선 · 선 중요도 · 덧긋기(러프 2, 0.59부터): 11의 G펜 획에 아래를 더합니다. 11의 손떨림 보정은 $\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의 들어감 · 빠짐).

    • 풀이 — 왜: 사람의 펜선은 들어간 뒤 힘이 오르고 가운데쯤 가장 세다가 풀리며, 꺾이는 곳에서 조금 더 누릅니다. 0.58은 진하기가 한결같고 굵기만 원래 선을 따라 “기계적”으로 보였습니다. 원리: 가운데가 가장 큰 가우스 혹 하나에 느린 사인 흔들림과 꺾임 가산을 더하고, 굵기에는 절반만 실어 원래 선 굵기를 크게 벗어나지 않게 합니다. - 선의 중요도와 진하기: 획을 따라 잰 지역 대비의 중앙값으로 $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})$입니다.
    • 풀이 — 왜: 러프에서 작가는 윤곽 · 얼굴처럼 중요한 선을 진하게, 옷 주름 · 머리카락 결 같은 곁선을 옅게 긋습니다. 원리: 둘레보다 뚜렷이 진한 선 · 굵은 선 · 긴 선 · 진한 선을 중요하게 보고, 가장 옅은 선도 70%로 남겨 끊겨 보이지 않게 합니다. - 덧긋기: 길이 $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$ 안에 있을 때만(나란히 붙은 사다리 가로선 · 잔가시) 지우고, 외톨이 짧은 획(안경테 · 손가락)은 남깁니다.
  13. 눈 · 입 상자(러프 2, 0.59e부터): 찾은 얼굴마다 눈 사이 거리 𝑑로 가로 $c_x \pm 1.0d$, 세로 $c_y - 0.35d$ ~ $c_y + 1.25d$인 상자(짐작한 얼굴은 짐작 얼굴 자리 전체) 안에서는 11의 지역 대비와 상관없이 $\ell \ge 0.15$를 선으로 삼고, 꽉 찬 덩어리 속을 비우지 않습니다(가운데 한 획).
    • 풀이 — 왜: 눈 · 입은 어두운 홍채 · 입속 위에 그려져 둘레보다 진하지 않고, 걸려도 덩어리 속을 비우면 테두리 고리만 남습니다(채점자가 “빠진 형태” · “눈이 고리”라고 적음). 원리: 눈 · 입 자리에서만 선 알파 문턱으로 선을 고르고, 나머지는 11의 지역 대비를 그대로 씁니다.

4.15 PSD 형식

부분 내용
머리 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

예전 PSD 층 이름 고치기(PSD.rename)

입력에 PSD · PSB(확장자 .psd · .psb, 또는 형식 image/vnd.adobe.photoshop)가 들어오면 그림을 나누지 않고 이것을 합니다. 예전 이름 → 지금 이름 목록(색트레스 → 색 트레이스)마다 아래처럼 바이트 단위로 읽어 이름이 든 층 레코드만 다시 쓰고, 바뀐 층이 있으면 같은 파일 이름으로 저장합니다(여러 개면 0.4초 간격). 아래에서 from은 예전 이름, to는 지금 이름입니다.

  1. 머리(26바이트) 뒤 색 모드 데이터 · 이미지 리소스를 길이만큼 건너뛰고 층과 마스크 정보로 들어갑니다. PSB는 층과 마스크 · 층 정보 · 채널 길이가 8바이트입니다.
  2. 8비트 층 정보와, 그 뒤 전역 추가 정보의 Lr16 · Lr32 · Layr(16 · 32비트 그림의 층 정보)를 같은 방법으로 읽습니다. 층마다 채널 길이를 더해 채널 데이터 전체 길이를 구하고, 추가 정보의 luni 이름 안에 든 from을 모두 to로 바꿉니다. 파스칼 이름도 EUC-KR로 읽어 from이 들어 있으면 바꾼 이름을 EUC-KR로 적습니다(255바이트 이하일 때. 한국어판 포토샵이 다시 저장한 파일). EUC-KR 표는 TextDecoder('euc-kr')로 0xA1A1~0xFEFE 두 바이트를 모두 풀어 거꾸로 만듭니다.
  3. 추가 정보 블록의 길이 폭(PSB에서 4 또는 8바이트)과 블록 뒤 채움(없음 · 2 · 4의 배수)은 프로그램마다 달라서, 블록 끝이 구역 끝이거나 다음 8BIM · 8B64 서명에 맞는 조합을 고릅니다(PSB에서 문서의 8바이트 키와 cinf는 8을 먼저, 나머지는 4를 먼저 시험).
  4. 바뀐 luni는 길이 $4 + 2n$을 4의 배수로 올려 다시 쓰고, 그 층의 추가 정보 길이, 층 정보 길이(원래 4의 배수였으면 4, 아니면 2의 배수로 채움. Lr16 등은 4의 배수), 층과 마스크 정보 길이만 새로 적습니다. 채널 픽셀 · 병합 이미지 · 이미지 리소스 · 전역 마스크는 바이트 그대로 옮깁니다. 바꿀 층이 없으면 저장하지 않고 그렇다고 알립니다.
    • 풀이 — 왜: 층 이름의 길이가 바뀌면 그 이름을 담은 블록들의 길이도 모두 다시 적어야 파일이 깨지지 않습니다. 원리: 새 luni 길이 $4 + 2n$을 4의 배수로 올리고, 그 차이만큼 바깥 블록(층 추가 정보 → 층 정보 → 층과 마스크 정보)의 길이를 차례로 고칩니다. 픽셀 데이터는 그대로 옮기므로 그림은 바뀌지 않습니다. 참고: R4 Adobe PSD

4.16 미리보기 합성

캔버스 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

화면

4.17 수동 보정 알고리즘

배경으로 보내기

선 알파는 모든 선화 층의 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는 시작 · 멈춤이 부정확해 채우기 출발점에서 뺍니다. 참고: 이 프로그램에서 정한 규칙

배경으로 보내기

캐릭터로 되돌리기: 배경 안에서 같은 방식(허용치 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.


← README