※ 주의사항 ※
본 블로그는 수업 내용을 바탕으로 제가 이해한 부분을 정리한 블로그입니다.
본 내용을 참고로만 보시고, 틀린 부분이 있다면 지적 부탁드립니다!
감사합니다😁
안녕하세요!!
오늘은 아래와 같은 내용을 확인해보겠습니다.
이미지 영상처리 알고리즘
화소영역 처리
경계선 검출
▣ 화소영역 처리 및 경계선 검출 시연 동영상 (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. 블러링
- 영상의 세밀한 부분을 제거하여 영상을 흐리게 하거나 부드럽게 하는 기술
- 영상의 세밀한 부분은 주파수 축에서는 고주파 성분인데, 블러링은 이 고주파 성분을 제거 (저역 통과 필터)
- 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. 샤프닝
- 블러링과는 반대되는 효과를 보이는 기법을 샤프닝 또는 영상 강화라고 함
- 고주파에 해당하는 상세한 부분을 더욱 강조하여 대비 효과를 증가 시킴 (흐린영상 개선)
- 샤프닝 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. 라플라시안 연산자
- 대표적인 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 마스크
댓글