5.17. 표준 커널 카탈로그

고전적인 이미지 처리에서는 반복적으로 등장하는 상당한 규모의 커널 가중치 패턴 카탈로그가 축적되어 왔습니다 – 에지 검출기, 샤프너, 엠보스, 스무더, 모션 블러 등 – 그리고 이들은 모두 morph()를 통해 실행됩니다. 각각은 짧고, 한 가지 일만 하며, 가중치의 기본 논리를 이해하고 나면 대부분 읽기가 어렵지 않습니다.

아래의 커널들은 별도의 표기가 없는 한 모두 3x3 이므로, 호출 시 모두 size=1 을 사용합니다. 각 커널의 가중치 구조는 그 옆에 설명되어 있는데, 왜 어떤 커널은 엠보싱을 하고 다른 커널은 샤프닝을 하는지에 대한 직관을 길러주는 것이 바로 가중치를 읽는 일이기 때문입니다.

5.17.1. 항등 커널

가능한 가장 단순한 커널은 항등(identity) 커널입니다 – 중앙에 1이 있고 나머지는 모두 0입니다:

identity = [0, 0, 0,
            0, 1, 0,
            0, 0, 0]

img.morph(1, identity)

각 출력 픽셀은 이웃의 중앙에서 값을 가져오며, 이는 같은 위치의 입력 픽셀입니다. 이미지는 변경 없이 그대로 통과합니다. 항등 커널은 필터로서 실용적인 용도는 없지만, 다른 모든 커널을 이해하는 데 유용한 기준이 됩니다. 항등이 아닌 모든 커널은 항등 커널에 어떤 변형을 더한 것이기 때문입니다.

중앙 가중치가 크고 주변에 작은 음수 가중치가 있는 커널은 주변값을 중앙값에서 뺍니다. 중앙 가중치가 0인 커널은 픽셀 자체를 무시하고 이웃들 간의 차이에만 반응합니다. 커널을 이런 식으로 읽는 것 – 중앙 가중치가 픽셀에 무엇을 하는지, 주변 가중치가 무엇을 더하거나 빼는지 – 이 그 효과를 예측하는 가장 빠른 방법입니다.

5.17.2. 에지 검출

에지 검출 커널은 밝기가 특정 방향으로 빠르게 변하는 위치에 강하게 반응하고, 밝기가 균일한 곳에서는 거의 0에 가까운 출력을 만듭니다. 이들은 가중치의 합이 0이 되는 부류입니다. 평탄한 영역(모든 픽셀이 같은 값)은 0 출력을 만드는데, 모든 양의 가중치가 동일한 크기의 음의 가중치에 의해 정확히 상쇄되기 때문입니다.

Sobel-x 가 대표적인 예입니다. 이는 수직 에지(좌/우 밝기 전환)를 검출합니다:

sobel_x = [-1,  0,  1,
           -2,  0,  2,
           -1,  0,  1]

img.morph(1, sobel_x, mul=0.25, add=128)

이에 대응하는 Sobel-y 는 동일한 패턴을 90도 회전한 것으로, 수평 에지(상/하 밝기 전환)를 검출합니다:

sobel_y = [-1, -2, -1,
            0,  0,  0,
            1,  2,  1]

Sobel-x의 가운데 행은 가중치가 -11 이 아니라 -22 입니다. 중앙 행의 추가 가중치는 커널에 에지 방향을 따라 약간의 내장 평활화 효과를 주어, 그러한 추가 크기를 생략하는 더 단순한 Prewitt 연산자보다 노이즈에 더 강건하게 만듭니다:

prewitt_x = [-1, 0, 1,
             -1, 0, 1,
             -1, 0, 1]

prewitt_y = [-1, -1, -1,
              0,  0,  0,
              1,  1,  1]

Prewitt은 모든 행에 동일한 가중치를 부여하므로, 단일 픽셀 노이즈에 더 민감해지는 대가로 Sobel보다 응답이 다소 더 날카롭습니다(커널 실행 비용은 동일합니다 – 컨볼루션은 가중치가 무엇이든 동일한 작업을 수행합니다). 강한 에지가 있는 깨끗한 이미지에서는 Sobel을 완벽하게 대체할 수 있습니다.

Scharr 는 반대 방향으로 갑니다. 가중치가 더 크고, 더 미세한 각도에서 에지 방향을 정확하게 검출하도록 조정되어 있습니다:

scharr_x = [-3,   0,  3,
            -10,  0, 10,
            -3,   0,  3]

img.morph(1, scharr_x, mul=0.0625, add=128)

mul=0.0625 제수(1/16)는 더 큰 곱의 합 이후에 출력을 다시 0255 범위 안으로 가져옵니다. Scharr은 애플리케이션이 가장 기하학적으로 충실한 그래디언트 응답을 필요로 하고 이를 위해 약간 더 많은 연산을 지불할 의향이 있을 때 적합한 선택입니다.

5.17.3. 라플라시안

라플라시안(Laplacian) 커널은 모든 방향의 에지에 동시에 반응합니다. Sobel들이 각각 한 축을 따라 밝기 변화를 검출하는 반면, 라플라시안의 대칭적인 가중치 패턴은 에지가 어느 방향으로 진행하든 동일하게 반응합니다:

laplacian_4 = [ 0, -1,  0,
               -1,  4, -1,
                0, -1,  0]

img.morph(1, laplacian_4, add=128)

구조는 다음과 같습니다: 중앙 가중치 4, 네 개의 수평/수직 이웃에 -1 가중치, 네 개의 대각선은 0 가중치. 커널의 합이 0이므로 평탄한 영역은 0 출력을 만듭니다. 밝기가 변하는 곳에서는 중앙값이 네 개의 기본 방향 이웃의 평균과 달라지며, 출력은 그 차이의 크기가 됩니다.

8-연결 변형은 대각선 이웃을 포함합니다:

laplacian_8 = [-1, -1, -1,
               -1,  8, -1,
               -1, -1, -1]

각 커널은 약간씩 다른 것을 검출합니다. 4-연결 버전은 수평 및 수직 에지에서 더 깨끗한 출력을 만들고, 8-연결 버전은 더 등방적입니다 – 모든 방향에서 동일하게 잘 반응합니다 – 하지만 약간 더 노이즈가 많은 출력을 만듭니다. 8-연결 커널은 에지를 시각화하는 데 쓰이는 용도에서 따와 outline 이라는 이름으로도 통용됩니다.

5.17.4. 샤프닝

샤프닝(sharpening) 커널은 항등 커널에 에지 응답 커널을 더한 것입니다. 출력은 원본 이미지에 에지의 복사본을 더한 것이므로, 고주파 특징이 매끄러운 내부 영역에 비해 증폭됩니다.

표준 4-연결 샤프닝 커널은 4-연결 라플라시안을 항등 커널에 더합니다:

sharpen = [ 0, -1,  0,
           -1,  5, -1,
            0, -1,  0]

img.morph(1, sharpen)

커널을 읽어 보면: 중앙 가중치는 identity (1) + Laplacian centre (4) = 5 이고, 주변값은 라플라시안과 일치합니다. 평탄한 영역은 중앙값의 5 * 1 - 4 * 1 = 1 배를 만들어냅니다 – 즉 항등입니다. 에지는 원본에 라플라시안 응답을 더한 값을 만듭니다. 가중치의 합이 1 이므로 muladd 는 기본값을 유지합니다.

더 강한 샤프닝을 위해서는 8-연결 변형이 더 나아갑니다:

sharpen_strong = [-1, -1, -1,
                  -1,  9, -1,
                  -1, -1, -1]

img.morph(1, sharpen_strong)

중앙 가중치 9identity (1) + Laplacian-8 centre (8) 입니다. 동일한 논리이지만 증폭이 더 크고, 센서 노이즈까지 증폭할 위험도 더 큽니다.

강한 샤프닝 커널은 본질적으로 unsharp=True 를 사용한 gaussian() 과 동일하며, 단지 언샤프 마스크 플래그를 통하지 않고 커널로 직접 표현된 것뿐입니다. 픽셀 수준의 동작은 같으며, 선택은 명명된 메서드의 편리함과 직접 조정한 커널의 세밀한 제어 사이의 문제입니다.

5.17.5. 엠보스

엠보스(emboss) 커널은 고전적인 이미지 편집기에서 볼 수 있는 측면 조명 효과를 만들어냅니다. 출력은 이미지가 부조로 돌출된 다음 한쪽 모서리에서 조명을 받은 것처럼 보입니다:

emboss = [-2, -1,  0,
          -1,  1,  1,
           0,  1,  2]

img.morph(1, emboss, add=128)

비결은 대각선을 가로지르는 비대칭성 입니다. 좌상단 모서리는 가장 큰 음의 가중치를, 우하단은 가장 큰 양의 가중치를 가지며, 모서리에서 모서리로 이어지는 대각선은 음수에서 1을 거쳐 양수로 진행합니다. 각 픽셀에서 커널은 본질적으로 “내 우하단의 밝기에서 내 좌상단의 밝기를 뺀 값”을 계산하는데, 이는 이미지가 그 방향으로 밝아지는 곳에서는 양수이고 어두워지는 곳에서는 음수가 됩니다. 128 을 더하면 부호 있는 출력이 중간 회색으로 재중심화되어 효과가 보이게 됩니다.

다른 대각선을 가로질러 비대칭성을 회전시키면 반대 방향에서 엠보싱을 합니다:

emboss_alt = [ 0,  1,  2,
              -1,  1,  1,
              -2, -1,  0]

img.morph(1, emboss_alt, add=128)

두 엠보스 방향은 조합하여 사용할 때 유용합니다 – 하나에서 다른 하나를 빼거나, 같은 이미지에 각각을 실행하고 응답을 비교하는 것 – 애플리케이션이 방향을 검출해야 할 때 그렇습니다.

5.17.6. 스무딩

스무딩 커널은 가중치의 합이 1이 되는(그리고 모두 음이 아닌) 부류입니다. 이러한 커널을 통과한 평탄한 영역은 동일한 평탄한 밝기를 만들어내는데, 커널이 픽셀값들의 차이를 증폭하는 대신 평균을 내기 때문입니다.

가장 단순한 것은 박스 블러(box blur) 로, 이는 정확히 mean() 이 계산하는 것입니다:

box_blur = [1, 1, 1,
            1, 1, 1,
            1, 1, 1]

img.morph(1, box_blur)

이 커널의 합은 9 이므로, 커널 합에 의한 자동 나눗셈은 곱의 합을 아홉 개의 이웃 픽셀에 대한 진짜 평균으로 바꿉니다. 실제로는 mean() 이 이 커널을 실행하는 더 나은 방법입니다 – 평균 계산만을 위해 최적화된 경로를 통해 같은 출력을 더 빠르게 만들어내는 반면, morph 는 범용 컨볼루션 기계를 실행합니다. 박스 블러가 카탈로그에 있는 이유는 다른 모든 스무딩 커널을 이해하기 위한 올바른 기준이기 때문입니다.

가우시안(Gaussian) 의 3x3 근사는 모서리보다 중앙과 기본 방향 이웃에 더 큰 가중치를 둡니다:

gaussian = [1, 2, 1,
            2, 4, 2,
            1, 2, 1]

img.morph(1, gaussian)

가중치는 파스칼 삼각형의 행 1, 2, 1 을 자기 자신과 외적한 것입니다. 중앙 가중치 4 는 중앙 픽셀이 자신의 출력에 가장 크게 기여하기 때문에 가장 크고, 모서리는 중앙에서 가장 멀기 때문에 1 입니다. 커널의 합은 16 이고, 커널 합에 의한 자동 나눗셈이 정규화를 처리합니다 – mul 인자가 필요 없습니다. 3x3 형태는 진짜 가우시안의 거친 근사이며 size=1 에서 gaussian() 과 구별할 수 없습니다. morph 형태는 주로 애플리케이션이 같은 패스에서 스무딩을 다른 연산과 조합하고자 할 때 유용합니다.

5.17.7. 모션 블러

모션 블러(motion-blur) 커널은 한 방향 을 따라 픽셀을 평균 내고, 수직 방향은 흐려지지 않은 채로 남깁니다. 가장 단순한 경우는 수평입니다:

motion_h = [0, 0, 0,
            1, 1, 1,
            0, 0, 0]

img.morph(1, motion_h)

가운데 행은 수평 축을 따라 세 픽셀을 평균 내고, 위쪽과 아래쪽 행은 0입니다. 커널의 합은 3 이므로, 커널 합에 의한 자동 나눗셈은 별도의 mul 없이 진짜 세 픽셀 평균을 만들어냅니다. 출력은 입력을 수평으로 번지게 한 복사본입니다 – 노출 중에 피사체가 옆으로 움직일 때 카메라가 포착하는 효과입니다. 수직 모션 블러는 동일한 패턴을 회전한 것입니다:

motion_v = [0, 1, 0,
            0, 1, 0,
            0, 1, 0]

대각선 모션 블러는 주 대각선을 사용합니다:

motion_diag = [1, 0, 0,
               0, 1, 0,
               0, 0, 1]

img.morph(1, motion_diag)

모션 블러 커널은 효과 로서(시각적 목적으로 프레임을 의도적으로 흐리게 하는 것)와 모션 아티팩트에 강건해야 하는 알고리즘을 위한 테스트 패턴 으로서(모션 블러가 적용된 입력에 알고리즘을 실행하여 여전히 올바른 답을 만드는지 확인하는 것) 모두 유용합니다.

5.17.8. 한눈에 커널 읽기

몇 가지 경험 법칙을 알면 새로운 커널을 보고 더 쉽게 읽을 수 있습니다:

  • 음이 아닌 가중치로 합이 1 ⇒ 스무딩(평균 밝기 보존).

  • 양수와 음수 가중치가 모두 있고 합이 0 ⇒ 에지 응답(평탄한 영역에서 0).

  • 큰 양의 중앙값과 작은 음의 주변값으로 합이 1 ⇒ 샤프닝(항등에 에지 응답을 더한 것).

  • 합이 1이면서 대각선을 가로질러 비대칭 ⇒ 엠보싱(모든 밝기 전환의 한쪽 면을 강조).

  • 합이 1이면서 한 축을 따라 집중 ⇒ 방향성 블러.

커널이 일치하는 이들 중 첫 번째 것이 보통 그것이 하는 일에 대한 올바른 추측입니다. 대부분의 유용한 커널은 가중치 패턴의 배치만으로도 식별할 수 있습니다.

표준 커널 중 어느 것도 애플리케이션이 원하는 것을 하지 못할 때, 다음 단계는 직접 하나를 조정하는 것입니다. 위의 규칙들과 mul / add 제어의 조합은 고전적인 머신 비전 파이프라인이 지금까지 원해 온 거의 모든 선형 패스를 포괄합니다. 거기서부터는 가중치를 시도하고, 출력을 보고, 반복하는 일의 문제입니다.