열방정식은 매질에서 열이 전달되는 과정을 기술한다. 열은 온도차이가 있을 때 전달되는 에너지로 온도 분포 $u(\vec{r},t)$가 주어진 경우 주변으로 나가는 열에너지는 온도의 gradient $\nabla u$에 비례한다. 

$$ \text{heat flux: }\quad  \vec{j}= \kappa (u)\nabla u$$

$\kappa$는 매질에서 열이 얼마나 잘 전도는지를 표현하는 일반적으로 $u$에 의존할 수 있다. 균일하고 등방적인 매질인 경우는 $\kappa$가 일정한 상수에 해당한다. 국소적으로 단위부피당 단위시간당 빠져나가는 열에너지는 thermal flux의 divergence에 비례하고 이 값이 0이 아니면 그 지점의 온도 변화를 만든다. 따라서 온도는 다음과 같은 방정식을 만족한다.

$$ u_t = \nabla \cdot \vec{j}  = \nabla\cdot(\kappa \nabla u)$$

이 방정식은 물질의 확산 현상에도 적용이 가능한데 이 경우 $u$는 물질의 밀도분포에 해당할 것이다.

$\kappa$가 상수인 경우 이 방정식은 선형방정식으로 초기 온도분포 $u(x,t=0)=f(x)$가 주어지면 해는 사이즈가 $\sigma = \sqrt{2\kappa t}$인 가우시안 커널(heat kernel 또는 Greeen function)과 초기 온도분포의 convolution으로 표현할 수 있다. 1차원 선형 열방정식의 해는 구체적으로 다음과 같이 표현된다.

\begin{align} u(x,t) =  \int \frac{1}{\sqrt{4\pi \kappa t}} e^{- (x-y)^2 / 4\kappa t } f(y) dy = ( G_{\sigma} * f)(x,t)\\G_\sigma (x) = \frac{1}{\sqrt{2\pi\sigma} }  e^{- x^2/2\sigma^2} \end{align}

열방정식은 뜨거운 커피가 담긴 잔을 방에 놓았을 때 방 안의 온도가 어떻게 변할지도 알 수 있게 해 준다. 물론 경험적으로 커피의 온도는 내려가고 방안은 잔의 주변부터 점차로 데워져서 나중에서는 모든 부분이 일정한 온도를 가지는 평형상태에 도달한다. 이는  가우시안 커널의 크기가 $\sqrt{t}$에 비례하므로 시간이 흐를수록 모든 지점에서 온도는 일정한 값으로 수렴할 것임을 쉽게 예측할 수 있다.

이 열방정식을 이미지에 대해서도 적용할 수 있다. 이 경우 초기 온도분포는 주어진 이미지의 명암값에 해당한다. 열방정식의 우변을 이미지에 적용하면 Gaussian 블러링에 해당하고, 시간은 블러링의 순차적인 적용 횟수를 의미한다. 이미지가 블러링 되는 경우 영상이 담고 있는 정보는 점점 잃게 되는데 이를 방지하기 위해서는 영상의 에지 정보를 보존하는 방법으로 열방정식을 변형시켜 보자. 영상에서 에지영역에서는 gradient 값이 커지므로 이 지점에서는 커널사이즈를 작게 (열방정식 관점에서는 열전도도를 줄임) 만들면 블러링 효과가 줄어들게 된다. 이를 위해서 다음과 같은 열전도도 함수를 도입하자.

$$ \kappa (u) = \frac {1}{1+ |\nabla u|^2/\lambda^2 }$$

여기서 $\lambda$은 contrast 파라미터로 에지의 세기가 $|\nabla| \gg \lambda$ 영역에서는 블러링의 영향을 거의 받지 않게 되어 열방정식을 적용하더라도 에지에 대한 정보 손실은 거의 일어나지 않는다. 그러나 $\kappa $가 상수가 아닌 경우는 열방정식은 비선형이므로 해를 명시적으로 쓸 수 없고 오직 수치해석적으로만 구할 수 있다.

먼저 미분연산자(Sobel operator: $\lambda$의 설정은 미분연산자의 normalization을 고려해야 함)을 이용해서 이미지의 gradient을 구하면 flux $\vec{j}$을 구할 수 있고, 다시 $j_x$에 대해서 $x-$방향 미분, $j_y$에 대해서 $y-$방향 미분을 해서 $\vec{j}$의 divergence, 즉 이미지의 일반화된 Laplace 연산 결과를 얻을 수 있다. 이 결과에 시간간격을 곱한 양을 이전 이미지에 더하여 새로운 이미지를 얻는다. 이 과정을 반복적으로 수행하면 된다.

$$ u(x,y, t+\Delta t) = u(x, y, t) + \Delta t \times \nabla \cdot (\kappa (|\nabla u| ) \vec{j})$$

$\kappa$가 상수일 때 $t \to \kappa t$로 시간변수를 바꾸면 열방정식은 차원이 없는 형태로 되고, $\kappa <1$이므로 시간간격은 $\Delta t \ll 1$를 만족하도록 선택해야 한다.

$\lambda=50,~\Delta t=0.01$일 때 5 스텝 간격으로의 변화

double conductivity(double gmagsq, double lambdasq) {
    return 1.0/ (1.0 + gmagsq/lambdasq);
}
int LaplaceImage(double *img, int w, int h, double lambda, double *laplace) {
    const double lambdasq = lambda * lambda;
    // Sobel gradient 3x3
    const int nn[] = { -w - 1, -w, -w + 1, -1, 0, 1, w - 1, w, w + 1};
    const int sobelX[] = { -1, 0, 1, -2, 0, 2, -1, 0, 1};
    const int sobelY[] = { -1, -2, -1, 0, 0, 0, 1, 2, 1};
    std::vector<double> Gx(w * h, 0);
    std::vector<double> Gy(w * h, 0);
    std::vector<double> Gmag2(w * h, 0);
    const int xmax = w - 1, ymax = h - 1;
    // gradient-x, gradient-y, and mag of gradient.
    for (int y = 1, pos = w; y < ymax; y++) { // pos = w; starting address;
        pos++; //skip x=0;
        for (int x = 1; x < xmax; x++, pos++) {
            double sx = 0, sy = 0;
            for (int k = 0; k < 9; k++) {
                double v = img[pos + nn[k]];
                sx += sobelX[k] * v;
                sy += sobelY[k] * v;
            }
            Gx[pos] = sx;
            Gy[pos] = sy;
            // gx^2 + gy^2;
            Gmag2[pos] = sx * sx + sy * sy;
        }
        pos++; // skip x=xmax;
    }
    // flux;
    for (int y = 1, pos = w; y < ymax; y++) { // pos = w; starting address;
        pos++; //skip x=0;
        for (int x = 1; x < xmax; x++, pos++) {
            double kappa = conductivity(Gmag2[pos], lambdasq);
            // multiply conductivity;
            Gx[pos] *= kappa;
            Gy[pos] *= kappa;
        }
        pos++; // skip x=xmax;
    }
    // divergence -> laplace;
    for (int y = 1, pos = w; y < ymax; y++) { // pos = w; starting address;
        pos++; //skip x=0;
        for (int x = 1; x < xmax; x++, pos++) {
            double sx = 0, sy = 0;
            for (int k = 0; k < 9; k++) {
                sx += sobelX[k] * Gx[pos + nn[k]];
                sy += sobelY[k] * Gy[pos + nn[k]];
            }
            laplace[pos] = sx + sy;
        }
        pos++; // skip x=xmax;
    }
    return 1;
}
void EpSmooth(BYTE *image, int w, int h, BYTE *out) {
    const double lambda = 50.0;
    const double dt = 0.01; //time step;
    const int maxiter = 100;
    std::vector<double> fimage(w * h, 0);
    std::vector<double> laplace(w * h, 0);
    for (int k = fimage.size(); k-- > 0;) fimage[k] = image[k];
    for (int iter = maxiter; iter-- > 0;) {
        LaplaceImage(&fimage[0], w, h, lambda, &laplace[0]);
        // update image;
        for (int k = fimage.size(); k-- > 0; ) 
            fimage[k] += dt * laplace[k];   
    }
    // preparing output;
    for (int k = fimage.size(); k-- > 0; ) {
        int a = int(fimage[k]);
        out[k] = a > 255 ? 255: a < 0 ? 0: a;
    }
}
728x90

'Image Recognition' 카테고리의 다른 글

Image Matting: Knockout method  (1) 2024.07.16
Anisotropic Diffusion Filter (2)  (0) 2024.02.23
Watershed Segmentation  (0) 2021.02.27
Local Ridge Orientation  (0) 2021.02.21
Contrast Limited Adaptive Histogram Equalization (CLAHE)  (3) 2021.02.15
,

Edge를 보존하는 low-pass filter로 주어진 픽셀 값을 그 위치에서 edge 방향을 따라 구한 평균값으로 대체한다. 방향은 8방향 (45도) 기준이다. Edge의 방향은 그림처럼 중심 픽셀을 기준으로 주변의 8개를 픽셀을 이용해서 45도씩 회전하면서  차이(평균 변화율)를 계산하여 가장 작은 값을 주는 방향으로 잡는다. 중심 픽셀이 line[1][1] 일 때

     0도 edge: abs(line[1][0] - line[1][2])가 최소

   45도 edge: abs(line[0][0] - line[2][2])가 최소

   90도 edge: abs(|ine[0][1] - line[2][1])가 최소

 135도 edge: abs(line[0][2] - line[2][0])가 최소 

Edge 방향의 3 픽셀 평균값으로 중심 픽셀 값을 대체하면 edge는 보존하면서 mean filter를 적용한 효과를 얻을 수 있다.

void smooth_ep_3x3(BYTE *src, int width, int height, BYTE* dst) {
    const int xend = width - 2, yend = height - 2;
    BYTE *line[3];
    line[0] = src; line[1] = line[0] + width; line[2] = line[1] + width;
    BYTE *dst_line = dst + width;        // starting dst row;
    for (int y = 0; y < yend; ++y) {
        for (int x = 0; x < xend; ++x) {
            int diff1 = line[1][x] - line[1][x + 2];
            if (diff1 < 0) diff1 = - diff1;
            int diff2 = line[0][x] - line[2][x + 2];
            if (diff2 < 0) diff2 = - diff2;
            int diff3 = line[0][x + 1] - line[2][x + 1];
            if (diff3 < 0) diff3 = - diff3;
            int diff4 = line[0][x + 2] - line[2][x];
            if (diff4 < 0) diff4 = - diff4;
            
            if (diff1 <= diff2 && diff1 <= diff3 && diff1 <= diff4)         //0-도
                dst_line[x + 1] = (line[1][x] + line[1][x + 1] + line[1][x + 2]) / 3;
            else if (diff2 <= diff3 && diff2 <= diff4)                      //45-도
                dst_line[x + 1] = (line[0][x] + line[1][x + 1] + line[2][x + 2]) / 3;
            else if (diff3 <= diff4)                                        //90-도;
                dst_line[x + 1] = (line[0][x + 1] + line[1][x + 1] + line[2][x + 1]) / 3;
            else                                                            //135-도;
                dst_line[x + 1] = (line[0][x + 2] + line[1][x + 1] + line[2][x]) / 3;
        }   
        dst_line += width; //move to next dst line;
        // increases src line ptr;
        BYTE *tptr = line[2] + width;
        line[0] = line[1]; line[1] = line[2]; line[2] = tptr;
    }
};

9번 반복 적용 결과: 지문처럼 규칙적인 패턴이 나오는 경우는 Gabor 필터를 이용하면 보다 좋은 결과를 얻을 수 있다.

네이버 블로그 이전

iteration에 따른 correlation의 계산:

iter=0, corr w.r.t original=0.925144, corr w.r.t previous=0.925144
iter=1, corr w.r.t original=0.903661, corr w.r.t previous=0.985120
iter=2, corr w.r.t original=0.888224, corr w.r.t previous=0.994821
iter=3, corr w.r.t original=0.876582, corr w.r.t previous=0.997403
iter=4, corr w.r.t original=0.866254, corr w.r.t previous=0.998183
iter=5, corr w.r.t original=0.856620, corr w.r.t previous=0.998596
iter=6, corr w.r.t original=0.847365, corr w.r.t previous=0.998841
iter=7, corr w.r.t original=0.838400, corr w.r.t previous=0.998981
iter=8, corr w.r.t original=0.829703, corr w.r.t previous=0.999088

728x90
,

 

pami05.pdf
다운로드

방 안에 뜨거운 물체가 놓이면 그 열은 시간이 지나면서 사방으로 퍼져나간다. 열원에서 주위로 열이 시간에 따라 전달이 되는 프로세스를 기술하는 방정식이 열(전도)방정식이다. 이것을 이미지 필터링에서도 적용이 가능하다. 이미지에 나타난 각각의 노이즈를 열원으로 보면 주변으로 열의 전달은 열원의 온도를 낮추게 되는데 이를 이미지에서 노이즈를 제거하는 filtering 과정으로 생각할 수 있다. 그러나 열방정식은 방향성이 없는 등방성 확산(isotropic diffusion process)을 기술한다. 이미지에 열전도 방정식을 바로 적용하면 이미지의 블러링과 유사한 효과를 만든다. filtering를 하더라도 에지를 보존해야 이미지의 정보 손실이 적게 된다. 에지 영역에서는 gradient의 공분산 행렬($\Sigma$)의 고윳값의 차이가 크고, 노이즈나 corner 근방에서는 두 고윳값의 크기가 비슷하다. 따라서, 에지를 보존하기 위해서는 공간으로 확산하는 역할을 하는 이미지의 Laplace 항 ($\nabla^2 I = I_{xx} + I_{yy}$)을 gradient의 공분산 행렬의 고유 벡터와 고유치, 그리고 이미지의 hessian 행렬 등을 조합하여서 불변인 형태를 만들어야 한다. 여러 가능한 후보군 중에 하나의 예를 들면

$$ I(t+dt) - I(t) = f_1(s) \text{Tr}({\bf  U U^T H}) + f_2(s) \text{Tr}({\bf V V^T H}) $$

$$ {\bf H}=\begin{pmatrix} I_{xx} & I_{xy } \\ I_{xy} & I_{yy} \end{pmatrix}, \quad \nabla^2 I = \text{Tr} ({\bf H}) $$

여기서, $\bf U, V$는 그라디엔트 공분산 행렬의 두 고유 벡터이고, $s=\lambda_1 + \lambda_2$는 고유값의 합이다. $f_1$과 $f_2$의 함수 형태를 고유치가 큰 방향에서는 억제되고, 고유치가 작은 방향에서는 덜 억제가 되도록 적절히 선택하는 경우에 에지를 보존하는 필터링의 효과를 얻을 수 있다.

아래의 예는 이 식을 써서 300번 iteration을 한 결과이다. 원본 이미지는 첨부한 논문에서 추출하였다.

사용자 삽입 이미지

https://kipl.tistory.com/563

 

Anisotropic Diffusion Filter

열방정식에서 매질에서 열의 확산은 특별히 선호된 방향이 없이 모든 방향에 대해서 동등하다. 물론 매질의 열전도도가 다르면 국속적으로 열확산이 균일하지 않을 수는 있어도 방향을 따지는

kipl.tistory.com

728x90

'Image Recognition' 카테고리의 다른 글

Retinex 알고리즘 관련 자료  (1) 2009.04.29
Spline Based Snake  (0) 2008.08.15
Rolling Ball Transformation  (0) 2008.08.09
Mean Shift Filter  (5) 2008.08.06
Chamfer Match  (0) 2008.08.01
,