본문 바로가기
대한상공회의소 스마트팩토리 교육/C# 프로그래밍

[시각화 프로그래밍] Gray Scale Image Processing Using C# «수업-4» : 이미지 영상처리 알고리즘 (화소영역 처리 / 경계선 검출)

by 나는영하 2022. 4. 10.

※ 주의사항 

본 블로그는 수업 내용을 바탕으로 제가 이해한 부분을 정리한 블로그입니다.
본 내용을 참고로만 보시고, 틀린 부분이 있다면 지적 부탁드립니다!

감사합니다😁

 

안녕하세요!!

오늘은 아래와 같은 내용을 확인해보겠습니다.

 

이미지 영상처리 알고리즘

화소영역 처리

경계선 검출


▣ 화소영역 처리 및 경계선 검출 시연 동영상 (C++)

 

화소영역 처리 및 경계선 검출 시연 동영상 (C#)


1. 이미지 영상처리 알고리즘 - ④ : (화소영역 처리)

※ 화소영역 처리 : 

입력 화소값 뿐만 아니라 그 주위의 화소값도 함께 고려해서 출력 화소값을 결정

1-1. 화소영역 처리 과정 및 공통 코드

(1) 1단계 : 회선 계산용 MASK 크기 및 값 입력

- 화소영역 처리를 하기 위한 MASK의 크기와 값을 일력 받는다.

- 화소영역의 기능(엠보싱, 블러링, 경계선 검출 등)마다 MASK의 크기와 값만 바뀐다.
→ 유사 연산자와 차 연산자를 제외하곤 모든 기능이 MASK값과 크기만 변한다.

	    const int MSIZE = 3;
            double[,] mask = { { -1.0, 0.0, 0.0},
                                { 0.0, 0.0, 0.0},
                                { 0.0, 0.0, 1.0} }; // 엠보싱 마스크

 

(2) 2단계 : 임시 입출력 배열 생성 및 값 입력

- 임시 입출력 이미지용 배열 생성

- 임시 입력 배열의 Size = 입력 이미지 Size + (MASK Size - 1)

- 임시 입력 배열의 가운데에 입력 이미지의 값을 넣고 나머지 외곽은 임의의 숫자로 채운다.

	    // 임시 입출력 메모리 확보
            // 기본 이미지 크기보다 위아래 1씩 더 크게
            double[,] tmpInput = new double[inH + 2, inW + 2]; 
            double[,] tmpOutput = new double[outH, outW];
            
            // 임시 입력 초기화 (0 or 127 or 평균값)
            for (int i = 0; i < inH + 2; i++)
            {
                for (int k = 0; k < inW + 2; k++)
                {
                    tmpInput[i, k] = 127.0;
                }
            }
            // 입력 --> 임시 입력 (임시 배열의 가운데에 위치하기)
            for (int i = 0; i < inH; i++)
            {
                for (int k = 0; k < inW; k++)
                {
                    tmpInput[i + 1, k + 1] = inImage[i, k];
                }
            }

 

(3) 3단계 : 회선 연산 수행

- MASK 값과 tmpInImage 화소점 값 간의 회선 연산 수행

- 회선 연산 결과는 tmpOutImage에 저장

 	    // 회선 연산 (mask 값을 대입해서 연산)
            for (int i = 0; i < inH; i++)
            {
                for (int k = 0; k < inW; k++)
                {
                    // 한점에 대한 마스크(3X3 MASK) 연산
                    double S = 0.0;
                    for (int m = 0; m < MSIZE; m++)
                        for (int n = 0; n < MSIZE; n++)
                            S += mask[m, n] * tmpInput[(m + i), (n + k)];

                    tmpOutput[i, k] = S;
                }

 

(4) 4단계 : 회선 연산 결과 가공 및 출력

- MASK 값의 총합이 0이면 tmpOutImage의 각 화소점 값에 127을 더해준다.
→MASK 값의 총합이 0이면 출력이미지가 어두워져 화소영역처리 결과가 잘 보이지 않는다.

	    for (int i = 0; i < outH; i++)
                for (int k = 0; k < outW; k++)
                    tmpOutput[i, k] += 127;

            // 임시 출력 --> 출력
            for (int i = 0; i < outH; i++)
                for (int k = 0; k < outW; k++)
                {
                    if (tmpOutput[i, k] < 0)
                        outImage[i, k] = 0;
                    else if (tmpOutput[i, k] > 255)
                        outImage[i, k] = 255;
                    else
                        outImage[i, k] = (byte)(tmpOutput[i, k]);
                }

 

 


1-2. 엠보싱

- 입력 영상을 양각 형태로 보이게 하는 기술
- 각 기능 코드 형태는 마스크의 크기 및 값만 바뀌고 나머지는 "1-1. 공통코드"와 동일하다.

 

✔ 엠보싱 마스크 크기 및 값

	    const int MSIZE = 3;
            double[,] mask = { { -1.0, 0.0, 0.0},
                                { 0.0, 0.0, 0.0},
                                { 0.0, 0.0, 1.0} }; // 엠보싱 마스크

1-3. 블러링

(좌) 블러링 - Value : 5 / (우) 블러링 - Value : 17

- 영상의 세밀한 부분을 제거하여 영상을 흐리게 하거나 부드럽게 하는 기술

- 영상의 세밀한 부분은 주파수 축에서는 고주파 성분인데, 블러링은 이 고주파 성분을 제거 (저역 통과 필터)

- WindowsForm을 통해 사용자로부터 블러링 마스크 크기를 받아서 블러링의 효과 조절 가능!!

- 임시 입력 이미지의 너비(폭) =  (입력 이미지의 너비(폭) + MASK의 너비(폭) - 1)

 

✔ 블러링 마스크 크기 및 값

	    blurrInputForm bif = new blurrInputForm(); // 서브 폼 준비
            bif.ShowDialog();
            int MSizeValue = bif.blurrValue;

            double[,] mask = new double[MSizeValue, MSizeValue];
            for (int i = 0; i < MSizeValue; i++)
            {
                for (int k = 0; k < MSizeValue; k++)
                {
                    mask[i, k] = 1.0 / (double)(MSizeValue * MSizeValue); // 블러링 마스크 : 합계 1
                }
            }

 

※ Blurring Mask Size Input Form

- 9개의 버튼을 통해 사용자가 MASK의 사이즈를 선택할 수 있음.

- MASK 사이즈가 커질수록 블러링의 효과가 커져서 사진이 더 흐리게 된다.

- buttonA_Click()의 코드는 해당 버튼을 누르면 blurrValue 값을 반환하는 형태로 구성되어 있음.
  (A : 3, 5, 7, .... 19)

 

 


1-4. 가우시안 필터(저역 통과 필터)

- 영상의 세세한 부분을 제거하여 부드럽게 하므로 스무딩 처리라고도 함

- 스무딩 처리에 사용되는 대표적인 저역통과 필터로 가우시안 필터가 있음.

 

✔ 가우시안 필터 마스크 크기 및 값

	    const int MSIZE = 3;
            double[,] mask = { { 1/16.0, 1/8.0, 1/16.0},
                                { 1/8.0, 1/4.0, 1/8.0},
                                { 1/16.0, 1/8.0, 1/16.0} }; // 가우시안 필터 마스크 : 합계 1

1-5. 샤프닝

(좌) 샤프닝 - Value : 5 / (우) 샤프닝 - Value : 9

- 블러링과는 반대되는 효과를 보이는 기법을 샤프닝 또는 영상 강화라고 함

- 고주파에 해당하는 상세한 부분을 더욱 강조하여 대비 효과를 증가 시킴 (흐린영상 개선)

- 샤프닝 MASK의 중앙값이 5인 경우는 이웃 화소를 가로와 세로 한해서(4Pixel) 회선연산을 수행하지만

  중앙값이 9인 경우는 이웃 화소를 가로와 세로, 그리고 대각선까지(8Pixel) 회선 연산을 수행해서 더 뛰어난 효과 제공

 

✔ 샤프닝 - 1(MASK Value = 5) 마스크 크기 및 값

	    const int MSIZE = 3;
            double[,] mask =  { { 0.0, -1.0, 0.0 },
                                { -1.0,  5.0, -1.0 },
                                { 0.0, -1.0, 0.0 } };// 샤프닝 필터 마스크- 1: 합계 1

✔ 샤프닝 - 2(MASK Value = 9) 마스크 크기 및 값

	    const int MSIZE = 3;
            double[,] mask = { { -1.0, -1.0, -1.0 },
                               { -1.0,  9.0, -1.0 },
                               { -1.0, -1.0, -1.0 } }; // 샤프닝 필터 마스크 - 2: 합계 1

1-6. 고주파 통과 필터

- 고주파 필터는 영상 신호 성분 중 고주파 성분은 통과시키고 저주파 성분은 차단

- 필터 계수의 합은 0으로 샤프닝 회선 마스크 하고는 다르나, 나머지 동작 특성은 같음

- 샤프닝 된 영상과 비교하면 더 많은 저주파 성분이 제거되어 단지 경계 부분만 확인 가능

 

✔ 고주파 통과 필터 마스크 크기 및 값

	    const int MSIZE = 3;
            double[,] mask = { { -1/9.0, -1/9.0, -1/9.0 },
                               { -1/9.0,  8/9.0, -1/9.0 },
                               { -1/9.0, -1/9.0, -1/9.0} }; // 하이 패스 필터 마스크 : 합계 0

2. 이미지 영상처리 알고리즘 - : (경계선 검출)

※ 경계선 검출 : 화소영역 처리의 일부분에 해당되며 디지털 영상의 경계선을 찾아내는 기술

→ 경계선(에지) : 디지털 영상의 밝기가 낮은 값에서 높은 값 또는 높은 값에서 낮은 값으로 변하는 지점

→ 디지털 영상을 구성하는 객체 간의 경계( = 경계선)

 

※ 경계선 검출의 방법은 크게 논리연산(뺄셈)을 통한 검출기법과 미분을 이용한 검출 기법으로 나뉜다.

뺄셈을 통한 검출 기법 : 유사 연산자, 차연산자

미분을 통한 검출 기법 : 로버츠, 프리윗, 소벨 등등 대다수

2-1. 유사 연산자

- 가장 단순한 에지 검출 방법으로 화소를 감산한 값에서 최대 값을 결정하여 에지 검출

 (단점 : 시간 소요 ↑)

 

✔ 유사 연산자의 회선 연산 과정 코드

	    // 회선 연산 
            for (int i = 0; i < inH; i++) {
                for (int k = 0; k < inW; k++) {
                    // 뺄셈 연산을 통해 최대값 산출
                    double maxValue = 0.0;
                    for (int m = 0; m < MSIZE; m++) {
                        for (int n = 0; n < MSIZE; n++) {
                            // 블록의 가운데 값 - 블록의 주변 값의 절대값 중에서 최대값을 찾는다.
                            if (Math.Abs(tmpInput[i + 1, k + 1] - tmpInput[i + m, k + n]) >= maxValue)
                                maxValue = Math.Abs(tmpInput[i + 1, k + 1] - tmpInput[i + m, k + n]);
                        }
                    }
                    tmpOutput[i, k] = maxValue;
                }
            }

 


2-2. 차 연산자

- 유사 연산자의 계산시간이 오래 걸리는 단점을 보완해주는 기법

- 뺄셈 연산을 각각 유사 연산자는 8번 차 연산자는 4번 수행

 

✔ 차 연산자의 회선 연산 과정 코드

	    double maxValue = 0.0;
            double[] mask = new double[4];
            for (int i = 0; i < inH; i++) {
                for (int k = 0; k < inW; k++) {
                    // 뺄셈 연산을 통해 최대값 산출
                    maxValue = 0.0;
                    mask[0] = Math.Abs(tmpInput[i, k] - tmpInput[i + 2, k + 2]);
                    mask[1] = Math.Abs(tmpInput[i, k + 2] - tmpInput[i + 2, k]);
                    mask[2] = Math.Abs(tmpInput[i, k + 1] - tmpInput[i + 2, k + 1]);
                    mask[3] = Math.Abs(tmpInput[i + 1, k + 2] - tmpInput[i + 1, k]);
                    for (int m = 0; m < 4; m++) {
                        if (mask[m] > maxValue) maxValue = mask[m];
                    }
                    tmpOutput[i, k] = maxValue;
                }
            }

2-3. 로버츠 엣지

(좌) 로버츠 엣지 수평 / (중) 로버츠 엣지 수직 / (우) 로버츠 엣지 수평 + 수직

※ 1차 미분 회선 마스크는 각각 행 검출 마스크와 열 검출 마스크가 별도로 있으며,

수평 + 수직 엣지 이미지를 얻을려면 별도의 합치는 작업이 필요

- 로버츠 엣지 장점 : 크기가 작아 매우 빠른 속도로 동작

- 로버츠 엣지 단점 : 돌출된 값을 장 평균할 수 없으며, 잡음에 민감

 

✔ 로버츠 엣지 마스크 크기 및 값

	        double[,] maskRowNew = { { -1.0,  0.0,  0.0 },
                                       {  0.0,  1.0,  0.0 },
                                       {  0.0,  0.0,  0.0 } }; // 로버츠-수평 마스크
                double[,] maskColNew = { {  0.0,  0.0, -1.0 },
                                       {  0.0,  1.0,  0.0 },
                                       {  0.0,  0.0,  0.0 } }; // 로버츠-수직 마스크

✔ 수평 회선 연산 값과 수직 회선 연산값을 더하는 과정 (출력부)

 	    for (int i = 0; i < outH; i++)
                for (int k = 0; k < outW; k++)
                {
                    double maskValue1 = (tmpOutput[i, k]);
                    double maskValue2 = (tmpOutput2[i, k]);
                    double maskValue = maskValue1 + maskValue2;
                    if (maskValue < 0)
                        outImage[i, k] = 0;
                    else if (maskValue > 255)
                        outImage[i, k] = 255;
                    else
                        outImage[i, k] = (byte)(maskValue);
                }
            displayImage();

2-4. 프리윗 엣지

(좌) 프리윗 엣지 수평 / (중) 프리윗 엣지 수직 / (우) 프리윗 엣지 수평+수직

- 프리윗 엣지 장점 : 돌출된 값을 비교적 잘 평균화 한다.

- 프리윗 엣지 단점 : 대각선 보다 수평과 수직에 놓인 에지에 더 민감하다.

 

✔ 프리윗 엣지 마스크 크기 및 값

		double[,] maskRowNew = { { -1.0, -1.0, -1.0 },
                                         {  0.0,  0.0,  0.0 },
                                          {  1.0,  1.0,  1.0 } }; // 프리윗-수평 마스크
                double[,] maskColNew = { {  1.0,  0.0, -1.0 },
                                       {  1.0,  0.0, -1.0 },
                                        {  1.0,  0.0, -1.0 } }; // 프리윗 - 수직 마스크

2-5. 소벨 엣지

(좌) 소벨 엣지 수평 / (중) 소벨 엣지 수직 / (우) 소벨 엣지 수평+수직

- 소벨 엣지 장점 : 돌출된 값을 비교적 잘 평균화 한다.

- 소벨 엣지 단점 : 대각선 방향에 놓인 에지에 더 민감하다.

 

✔ 소벨 엣지 마스크 크기 및 값

		double[,] maskRowNew = { { -1.0, -2.0, -1.0 },
                                          {  0.0,  0.0,  0.0 },
                                       {  1.0,  2.0,  1.0 } }; // 소벨 - 수평 마스크
                double[,] maskColNew = { {  1.0,  0.0, -1.0 },
                                         {  2.0,  0.0, -2.0 },
                                       {  1.0,  0.0, -1.0 } }; // 소벨 - 수직 마스크

2-6. 라플라시안 연산자

(좌) 라플라시안 Value : 4 / (중) 라플라시안 Value : -8 / (우) 라플라시안 Value : 8

- 대표적인 2차 미분 연산자로 모든 방향의 에지를 강조

- 1차 미분의 회선 마스크는 행과 열 방향의 마스크가 각각 있었지만 2차 미분의 라플라시안 회선 마스크에는 행과 열방향이 합쳐져서 한개의 회선 마스크만 존재 한다.

 

✔ 라플라시안 연산자 마스크 크기 및 값

 	    const int MSIZE = 3;
            double[,] mask = new double[MSIZE, MSIZE];
            if (maskValue ==1)
            {
                double[,] maskNew = { {  0.0, -1.0,  0.0 },
                               { -1.0,  4.0, -1.0 },
                               {  0.0, -1.0,  0.0 } }; // 라플라시안(4) 마스크
                mask = maskNew;
            }
            else if (maskValue ==2)
            {
                double[,] maskNew = { {  1.0,  1.0,  1.0 },
                               {  1.0, -8.0,  1.0 },
                               {  1.0,  1.0,  1.0 } }; // 라플라시안(-8) 마스크
                mask = maskNew;
            }
            else if (maskValue ==3)
            {
                double[,] maskNew = { { -1.0, -1.0, -1.0 },
                               { -1.0,  8.0, -1.0 },
                               { -1.0, -1.0, -1.0 } }; // 라플라시안(8) 마스크
                mask = maskNew;
            }

2-7. LOG(Laplacian Of Gaussian)

- 잡음에 민감한 라플라시안 마스크를 이용한 엣지

- 가우시안 스무딩 필터링을 먼저 수행하고 그 결과 값에 라플라시안을 수행하는 방법

 

✔ LOG 연산자 마스크 크기 및 값

	    const int MSIZE = 5;
            double[,] mask = { {  0.0,  0.0, -1.0,  0.0,  0.0 },
                               {  0.0, -1.0, -2.0, -1.0,  0.0 },
                               { -1.0, -2.0, 16.0, -2.0, -1.0 },
                               {  0.0, -1.0, -2.0, -1.0,  0.0 },
                               {  0.0,  0.0, -1.0,  0.0,  0.0 } }; // Laplacian Of Gaussian 마스크

2-8. DOG(Difference Of Gaussian)

- 계산 시간이 많이 소요되는 LOG 연산자의 단점을 보안하기 위한 방식

- 각 가우시안 연산에 분산 값을 서로 다르게 주어 이 차를 이용해 에지를 구함

 

✔ DOG 연산자 마스크 크기 및 값

	    const int MSIZE = 9;
            double[,] mask = { {  0.0,  0.0,  0.0, -1.0, -1.0, -1.0,  0.0,  0.0,  0.0 },
                               {  0.0, -2.0, -3.0, -3.0, -3.0, -3.0, -3.0, -2.0,  0.0 },
                               {  0.0, -3.0, -2.0, -1.0, -1.0, -1.0, -2.0, -3.0,  0.0 },
                               { -1.0, -3.0, -1.0,  9.0,  9.0,  9.0, -1.0, -3.0, -1.0 },
                               { -1.0, -3.0, -1.0,  9.0, 19.0,  9.0, -1.0, -3.0, -1.0 },
                               { -1.0, -3.0, -1.0,  9.0,  9.0,  9.0, -1.0, -3.0, -1.0 },
                               {  0.0, -3.0, -2.0, -1.0, -1.0, -1.0, -2.0, -3.0,  0.0 },
                               {  0.0, -2.0, -3.0, -3.0, -3.0, -3.0, -3.0, -2.0,  0.0 },
                               {  0.0,  0.0,  0.0, -1.0, -1.0, -1.0,  0.0,  0.0,  0.0 } }; 
                               // Diffrence Of Gaussian 마스크

댓글