반응형
250x250
Notice
Recent Posts
Recent Comments
Link
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

폴크(FOLC)

알고리즘 - Edge 추출 방법 ( Frangi-Vesselness ) 본문

카테고리 없음

알고리즘 - Edge 추출 방법 ( Frangi-Vesselness )

folcjin 2025. 8. 13. 13:11
728x90
반응형

이미지(또는 볼륨)에서 선형/관 구조(line-/tube-like structure)를 강조하는 필터로, 핵심은 각 픽셀에서 Hessian 행렬(2차 미분)의 고유값을 보고 한 축으로는 휘도 변화가 강하고(굵직함) 직교 축으로는 변화가 약한(가늘게 길게) 패턴을 높은 점수를 준다.

 

목적: 이미지에서 길쭉한 선형 패턴 검출
적용 분야: 혈관 영상 분석, 스크래치/크랙 검출, 섬유 패턴 분석 등
핵심 아이디어:
- 픽셀 주변의 Hessian 행렬(2차 미분) 분석
- 고유값 패턴으로 선형성 판단
- 다양한 **스케일(σ)**에서 선 구조 탐색

 

장점
- 선형 구조에 특화, 노이즈 강인
- 다양한 굵기 대응 가능 (멀티스케일)

한계
- 교차/분기부 반응 약함
- 극성 혼합 시 두 번 계산 필요
- σ 범위와 파라미터에 따라 성능 차이 큼

예시
- 혈관 네트워크 추출
- 금속 표면 스크래치 검출
- 섬유/패턴 분석

 

 

 

소스 코드

// C++14 / OpenCV 4.x
#include <opencv2/opencv.hpp>
#include <algorithm>
#include <vector>
#include <cmath>

cv::Mat FrangiVesselnessMultiScaleOCV(const cv::Mat& src8or32f,
                                      const std::vector<double>& sigmas,
                                      bool bright_on_dark = false, // 밝은 선 찾기(true) / 어두운 선(false, 기본)
                                      double beta = 0.5,
                                      double c = 15.0)
{
    CV_Assert(!sigmas.empty());
    CV_Assert(src8or32f.channels() == 1);

    cv::Mat f;
    if (src8or32f.type() == CV_32F) {
        f = src8or32f.clone();
    } else {
        src8or32f.convertTo(f, CV_32F, 1.0 / 255.0);
    }

    cv::Mat V = cv::Mat::zeros(f.size(), CV_32F);
    const double beta2 = 2.0 * beta * beta;
    const double c2    = 2.0 * c * c;

    for (double sigma : sigmas) {
        if (sigma <= 0) continue;

        // 1) 가우시안으로 스무딩(스케일-스페이스)
        cv::Mat g;
        cv::GaussianBlur(f, g, cv::Size(), sigma, sigma, cv::BORDER_REFLECT101);

        // 2) Hessian 근사: 2차/혼합 미분 (스무딩된 영상에 Sobel 적용)
        cv::Mat Ixx, Iyy, Ixy;
        // d2/dx2, d2/dy2, d2/dxdy
        cv::Sobel(g, Ixx, CV_32F, 2, 0, 3, 1, 0, cv::BORDER_REFLECT101);
        cv::Sobel(g, Iyy, CV_32F, 0, 2, 3, 1, 0, cv::BORDER_REFLECT101);
        cv::Sobel(g, Ixy, CV_32F, 1, 1, 3, 1, 0, cv::BORDER_REFLECT101);

        // 3) 스케일 정규화 (sigma^2)
        float s2 = static_cast<float>(sigma * sigma);
        Ixx *= s2; Iyy *= s2; Ixy *= s2;

        // 4) Frangi vesselness (단일 스케일) + 5) 멀티스케일 max-projection
        const int rows = f.rows, cols = f.cols;
        for (int y = 0; y < rows; ++y) {
            const float* ax = Ixx.ptr<float>(y);
            const float* bx = Ixy.ptr<float>(y);
            const float* dx = Iyy.ptr<float>(y);
            float*       vx = V  .ptr<float>(y);
            for (int x = 0; x < cols; ++x) {
                double a = ax[x], b = bx[x], d = dx[x];
                // eigenvalues of [[a,b],[b,d]] with |λ1| ≤ |λ2|
                double tr = a + d;
                double det = a * d - b * b;
                double disc2 = 0.25 * tr * tr - det;
                double disc = std::sqrt(disc2 > 0.0 ? disc2 : 0.0);
                double l1 = 0.5 * tr - disc;
                double l2 = 0.5 * tr + disc;

                // polarity (밝은 선/어두운 선)
                if (bright_on_dark) { if (l2 < 0.0) continue; }
                else                 { if (l2 > 0.0) continue; }

                double Rb = (l2 == 0.0) ? 0.0 : std::abs(l1) / std::abs(l2); // blob 대비 선형성
                double S2 = l1*l1 + l2*l2;                                   // ‖H‖_F^2
                double v  = std::exp(-(Rb*Rb)/beta2) * (1.0 - std::exp(-S2/c2));

                if (v > vx[x]) vx[x] = static_cast<float>(v); // max over scales
            }
        }
    }

    // 6) (선택) 0~1 클램프
    cv::threshold(V, V, 1.0, 1.0, cv::THRESH_TRUNC);
    cv::threshold(V, V, 0.0, 0.0, cv::THRESH_TOZERO);
    return V;
}

 

#include <opencv2/opencv.hpp>
int main() {
    cv::Mat gray = cv::imread("input.png", cv::IMREAD_GRAYSCALE);
    std::vector<double> sigmas = {1.0, 2.0, 3.0, 4.0};

    // 어두운 선(스크래치) 강조: bright_on_dark=false
    cv::Mat vessel = FrangiVesselnessMultiScaleOCV(gray, sigmas, /*bright_on_dark=*/false, 0.5, 15.0);

    // 보기 좋게 0~255로 변환해 저장
    cv::Mat vis; vessel.convertTo(vis, CV_8U, 255.0);
    cv::imwrite("vessel.png", vis);
    return 0;
}

728x90
반응형
사업자 정보 표시
사업자 등록번호 : -- | TEL : --