더북(TheBook)
소스 11-4 캐니 엣지 검출 구현 함수(IppFeature.cpp)
#include <vector>
#include "IppFilter.h"

const double PI = 3.14159265358979323846;
const float PI_F = 3.14159265358979323846f;

#define CHECK_WEAK_EDGE(x, y) \
    if (pEdge[y][x] == WEAK_EDGE) { \
        pEdge[y][x] = STRONG_EDGE; \
        strong_edges.push_back(IppPoint(x, y)); \
    }

void IppEdgeCanny(IppByteImage& imgSrc, IppByteImage& imgEdge, float sigma, float th_low, float th_high)
{
    register int i, j;

    int w = imgSrc.GetWidth();
    int h = imgSrc.GetHeight();

    // 1. 가우시안 필터링

    IppFloatImage imgGaussian(w, h);
    IppFilterGaussian(imgSrc, imgGaussian, sigma);

    // 2. 그래디언트 구하기 (크기 & 방향)

    IppFloatImage imgGx(w, h); // gradient of x
    IppFloatImage imgGy(w, h); // gradient of y
    IppFloatImage imgMag(w, h); // magnitude of gradient

    float** pGauss = imgGaussian.GetPixels2D();
    float** pGx = imgGx.GetPixels2D();
    float** pGy = imgGy.GetPixels2D();
    float** pMag = imgMag.GetPixels2D();

    for (j = 1; j < h - 1; j++)
    for (i = 1; i < w - 1; i++)
    {
        pGx[j][i] = -pGauss[j - 1][i - 1] - 2 * pGauss[j][i - 1] - pGauss[j + 1][i - 1]
            + pGauss[j - 1][i + 1] + 2 * pGauss[j][i + 1] + pGauss[j + 1][i + 1];
        pGy[j][i] = -pGauss[j - 1][i - 1] - 2 * pGauss[j - 1][i] - pGauss[j - 1][i + 1]
            + pGauss[j + 1][i - 1] + 2 * pGauss[j + 1][i] + pGauss[j + 1][i + 1];

        pMag[j][i] = sqrt(pGx[j][i] * pGx[j][i] + pGy[j][i] * pGy[j][i]);
    }

    // 3. 비최대 억제
    // 국지적 최대를 구함과 동시에 이중 임계값을 적용하여 strong edge와 weak edge를 구한다.

    imgEdge.CreateImage(w, h);
    BYTE** pEdge = imgEdge.GetPixels2D();

    enum DISTRICT { AREA0 = 0, AREA45, AREA90, AREA135, NOAREA };

    const BYTE STRONG_EDGE = 255;
    const BYTE WEAK_EDGE = 128;

    std::vector<IppPoint> strong_edges;

    float ang;
    int district;
    bool local_max;
    for (j = 1; j < h - 1; j++)
    for (i = 1; i < w - 1; i++)
    {
        // 그래디언트 크기가 th_low보다 큰 픽셀에 대해서만 국지적 최대 검사
        // 국지적 최대인 픽셀에 대해서만 강한 엣지 또는 약한 엣지로 설정
        if (pMag[j][i] > th_low)
        {
            // 그래디언트 방향 계산 (4개 구역)
            if (pGx[j][i] != 0.f)
            {
                ang = atan2(pGy[j][i], pGx[j][i]) * 180 / PI_F;
                if (((ang >= -22.5f) && (ang < 22.5f)) || (ang >= 157.5f) || (ang < -157.5f))
                    district = AREA0;
                else if (((ang >= 22.5f) && (ang < 67.5f)) || ((ang >= -157.5f) && (ang <
                    -112.5f)))
                    district = AREA45;
                else if (((ang >= 67.5) && (ang < 112.5)) || ((ang >= -112.5) && (ang < -67.5)))
                    district = AREA90;
                else
                    district = AREA135;
            }
            else
                district = AREA90;

            // 국지적 최대 검사
            local_max = false;
            switch (district)
            {
            case AREA0:
                if ((pMag[j][i] >= pMag[j][i - 1]) && (pMag[j][i] > pMag[j][i + 1]))
                    local_max = true;
                break;
            case AREA45:
                if ((pMag[j][i] >= pMag[j - 1][i - 1]) && (pMag[j][i] > pMag[j + 1][i + 1]))
                    local_max = true;
                break;
            case AREA90:
                if ((pMag[j][i] >= pMag[j - 1][i]) && (pMag[j][i] > pMag[j + 1][i]))
                    local_max = true;
                break;
            case AREA135:
            default:
                if ((pMag[j][i] >= pMag[j - 1][i + 1]) && (pMag[j][i] > pMag[j + 1][i - 1]))
                    local_max = true;
                break;
            }

            // 강한 엣지와 약한 엣지 구분
            if (local_max)
            {
                if (pMag[j][i] > th_high)
                {
                    pEdge[j][i] = STRONG_EDGE;
                    strong_edges.push_back(IppPoint(i, j));
                }
                else
                    pEdge[j][i] = WEAK_EDGE;
            }
        }
    }

    // 4. 히스테리시스 엣지 트래킹

    while (!strong_edges.empty())
    {
        IppPoint p = strong_edges.back();
        strong_edges.pop_back();

        int x = p.x, y = p.y;

        // 강한 엣지 주변의 약한 엣지는 최종 엣지(강한 엣지)로 설정
        CHECK_WEAK_EDGE(x + 1, y     )
        CHECK_WEAK_EDGE(x + 1, y + 1)
        CHECK_WEAK_EDGE(x      , y + 1)
        CHECK_WEAK_EDGE(x - 1, y + 1)
        CHECK_WEAK_EDGE(x - 1, y     )
        CHECK_WEAK_EDGE(x - 1, y - 1)
        CHECK_WEAK_EDGE(x     , y - 1)
        CHECK_WEAK_EDGE(x + 1, y - 1)
    }

    for (j = 0; j < h; j++)
    for (i = 0; i < w; i++)
    {
        // 끝까지 약한 엣지로 남아있는 픽셀은 모두 엣지가 아닌 것으로 판단
        if (pEdge[j][i] == WEAK_EDGE) pEdge[j][i] = 0;
    }
}
신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.