잡음 생성과 제거

2023. 5. 9. 14:57Computer/영상처리

잡음 생성

영상의 잡음 - 상의 픽셀 값에 추가되는 원치 않는 형태의 신호

잡음 생성1 - 소금&후추(Salt&Pepper) 잡음 생성

소금&후추 잡음은 입력 영상의 임의의 좌표 픽셀 값을 0 또는 255로 만드는 형태의 잡음이다. 

카메라 센서 자체의 에러, 광학 신호를 전기적 신호로 변환하는 과정에서의 에러

 

가우시안 잡음은 영상 내의 모든 픽셀에 대해 잡음을 추가하는 형태로 코드를 구현하였다. 그러나 소금&후추 잡음은 영상 내의 임의의 픽셀에 대해서만 그 값을 0 또는 255로 설정하면 된다.

 

void IppNoiseSaltNPepper(IppByteImage& imgSrc, IppByteImage& imgDst, int amount)
{
    int size = imgSrc.GetSize();
    imgDst = imgSrc;
    BYTE* pDst = imgDst.GetPixels();
    unsigned int seed = static_cast<unsigned int>(time(NULL));
    std::default_random_engine generator(seed);
    std::uniform_int_distribution<int> distribution(0, size - 1);
    int num = size * amount / 100;
    for (int i = 0; i < num; i++) {
        pDst[distribution(generator)] = (i & 0x01) * 255;
    }
}

 

잡음 생성2 - 가우시안 잡음 생성

가우시안 잡음 - 가우시안 분포 함수 형태를 따르는 잡음

예를 들어, 카메라에서 광학 신호를 전기적 신호로 변환하는 센서에 가우시안 잡음이 추가될 수 있음

평균값 필터 등 스무딩으로 효과를 감쇠시킬 수 있음

 

정상 분포(평균이 0이고, 표준 편차가 1인 가우시안 분포) 난수 10개 발생시키기 코드

#include <math.h>
#include <time.h>
#include <stdlib.h>
#include <random>
#include <algorithm>
void IppNoiseGaussian(IppByteImage& imgSrc, IppByteImage& imgDst, int amount)
{
    int size = imgSrc.GetSize();
    imgDst = imgSrc;
    BYTE* pDst = imgDst.GetPixels();
    unsigned int seed = static_cast<unsigned int>(time(NULL));
    std::default_random_engine generator(seed);
    std::normal_distribution<double> distribution(0.0, 1.0);
    double rn;
    for (int i = 0; i < size; i++) {
        rn = distribution(generator) * 255 * amount / 100;
        pDst[i] = static_cast<BYTE>(limit(pDst[i] + rn));
    }
}

 

잡음 제거를 위한 비선형 필터

 

두 필터는 마스크 연산 기반의 필터가 아니다.

잡음 제거 필터1 - 미디언 필터

미디언 필터median filter 입력 영상에서 주변 픽셀들의 값들을 오름 또는 내림 차순으로 정렬하여 그 중앙에 있는 값으로 픽셀 값을 대체하는 방식의 필터이다. 

미디언 필터는 마스크 값과 입력 픽셀의 값을 곱하여 선형 합으로 계산하는 필터가 아니기 때문에 비선형 공간적 필터링 기법으로 분류된다. 미디언 필터는 특히 소금&후추 잡음을 효과적으로 제거하는 능력을 가지고 있다.

더보기

소금과 후추 잡음을 효과적으로 제거하는 이유는? 미디언 필터는 소금과 후추 잡음과 같은 극단적인 값들을 효과적으로 제거하면서도 이미지의 구조를 보존하는 특징 때문에

  1. 미디언 필터의 작동 원리: 미디언 필터는 주변 픽셀들의 값 중 중간값(median)을 사용하여 현재 픽셀의 값을 대체하는 방식으로 작동합니다. 잡음이 있는 이미지에서는 잡음 픽셀 값이 주변 픽셀들의 값과 크게 차이가 나기 때문에 이를 효과적으로 필터링할 수 있습니다.
  2. 중간값의 불변성: 소금과 후추 잡음은 픽셀 값이 원래 이미지의 값과 크게 다르게 변화시킵니다. 이러한 잡음은 특정 영역에서 극단적인 값으로 나타나기 때문에 미디언 필터는 이를 중간값으로 대체하여 잡음을 제거할 수 있습니다. 이와 달리 다른 필터들은 평균값 등을 사용하므로 이러한 잡음을 효과적으로 제거하기 어렵습니다.
  3. 구조 보존: 미디언 필터는 잡음을 제거하는 동시에 이미지의 구조를 보존하는 경향이 있습니다. 다른 필터들이 잡음을 제거할 때 이미지의 경계나 선명도가 흐려지는 경우가 있지만, 미디언 필터는 중간값을 사용하기 때문에 이미지의 구조를 상당히 보존할 수 있습니다.

 

void IppFilterMedian(IppByteImage& imgSrc, IppByteImage& imgDst) {
	int w = imgSrc.GetWidth();
	int h = imgSrc.GetHeight();

	imgDst = imgSrc;

	BYTE** pSrc = imgSrc.GetPixels2D();
	BYTE** pDst = imgDst.GetPixels2D();

	int i, j;
	BYTE m[9];
	for (j = 1; j < h - 1; j++) {
		for (i = 1; i < w - 1; i++) {
			m[0] = pSrc[j - 1][i - 1]; m[1] = pSrc[j - 1][i]; m[2] = pSrc[j - 1][i + 1];
			m[3] = pSrc[j][i - 1]; m[4] = pSrc[j][i]; m[5] = pSrc[j][i + 1];
			m[6] = pSrc[j + 1][i - 1]; m[7] = pSrc[j + 1][i]; m[8] = pSrc[j + 1][i + 1];

			std::sort(m, m + 9);
			pDst[j][i] = m[4];
		}
	}
}

미디언 필터를 구현하기 위해서는 픽셀 값을 정렬하는 정렬 알고리즘을 사용해야 한다. 입력 영상 imgSrc에 대해 3×3 크기의 정방형 마스크를 이용한 미디언 필터링을 수행하고, 그 결과를 imgDst에 저장한다.

 

잡음 제거 필터2 - 비등방성 확산 필터

엣지 보전(Edge-preserving) 잡음 제거 필터의 하나, 비등방성 필터는 영상의 정보를 분석하여 각 방향의 필터링 정도를 다르게 결정한다. 비등방성: 각 방향의 필터링 정도가 다름

비등방성 확산은 원래 물리학에서 열이 전도되는 현상에 대한 연구에서 그 수식이 유래하였다. 

cf) 가우시안 필터는 영상의 엣지 부분 또한 부드럽게 만드는 등방성 필터이다.

프로파일(특정 y좌표에서 그레이스케일 ), 가우시안 필터 비등방성 확산 필터

void IppFilterDiffusion(IppByteImage& imgSrc, IppFloatImage& imgDst, float
lambda, float k, int iter)
{
    int w = imgSrc.GetWidth();
    int h = imgSrc.GetHeight();
    IppFloatImage imgCpy;
    imgCpy.Convert(imgSrc);
    imgDst = imgCpy;
    float** pCpy = imgCpy.GetPixels2D();
    float** pDst = imgDst.GetPixels2D();
    //iter횟수만큼 비등방성 확산 알고리즘 수행
    register int i, x, y;
    float gradn, grads, grade, gradw;
    float gcn, gcs, gce, gcw;
    float k2 = k * k;
    for (i = 0; i < iter; i++) {
        for(y = 1; y <h-1; y++)
            for (x = 1; x < w - 1; x++) {
                gradn = pCpy[y - 1][x] - pCpy[y][x];
                grads = pCpy[y + 1][x] - pCpy[y][x];
                grade = pCpy[y][x-1] - pCpy[y][x];
                gradw = pCpy[y][x+1] - pCpy[y][x];
                gcn = gradn / (1.0f + gradn * gradn / k2);
                gcs = grads / (1.0f + grads * grads / k2);
                gce = grade / (1.0f + grade * grade / k2);
                gcw = gradw / (1.0f + gradw * gradw / k2);
                pDst[y][x] = pCpy[y][x] + lambda * (gcn + gcs + gce + gcw);
            }
    if (i < iter - 1)
        memcpy(imgCpy.GetPixels(), imgDst.GetPixels(), sizeof(float) * w * h);
    }
}