반응형
250x250
Notice
Recent Posts
Recent Comments
Link
«   2025/06   »
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
Archives
Today
Total
관리 메뉴

폴크(FOLC)

치수 계산하기 - edge score 2 본문

카테고리 없음

치수 계산하기 - edge score 2

folcjin 2025. 6. 19. 14:56
728x90
반응형

1D 신호에서 자동 CD 측정을 위한 최적 점수 조합 기반 알고리즘
- Gradient Magnitude (강도)
- Sharpness (2차 도함수 기반 집중도)
-
Noise Contrast Ratio (SNR 기반 신뢰도)
-
Pair Symmetry (± gradient 쌍의 균형)
이를 바탕으로 가장 신뢰도 높은 edge 쌍을 찾아 subpixel 정밀도로 CD를 측정한다.

 

요약

이미지 로딩 → 선택 row 프로파일 추출 → 1D gradient 및 score 계산 → subpixel edge 위치 보간 + 점수 평가 → 양/음 edge 쌍 찾기 → 점수 높은 쌍 선택 → CD 계산 및 출력

 

예제

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>

using namespace std;
using namespace cv;

enum class EdgeDirection { POSITIVE, NEGATIVE };

struct EdgePoint {
    float position;         // subpixel 위치
    float gradient;         // 1차 미분
    float sharpness;        // 2차 미분
    float ncr;              // local contrast 기반 신뢰도
    float score;            // 총합 점수
    EdgeDirection direction;
};

// Subpixel 위치 보간 (Parabola fitting)
float subpixelOffset(float g_prev, float g_curr, float g_next) {
    float denom = g_prev - 2 * g_curr + g_next;
    if (fabs(denom) < 1e-5) return 0.0f;
    return 0.5f * (g_prev - g_next) / denom;
}

// 5픽셀 RMS 계산
float localRMS(const vector<float>& signal, int idx) {
    float sum = 0.0f;
    for (int i = -2; i <= 2; ++i) {
        float diff = signal[idx + i] - signal[idx];
        sum += diff * diff;
    }
    return sqrt(sum / 5.0f);
}

// 점수 계산
EdgePoint makeEdgePoint(const vector<float>& signal, int i) {
    float g = 0.5f * (signal[i + 1] - signal[i - 1]);
    float sharp = fabs(signal[i + 1] - 2 * signal[i] + signal[i - 1]);
    float rms = max(1e-5f, localRMS(signal, i));
    float ncr = fabs(g) / rms;
    float offset = subpixelOffset(signal[i - 1], signal[i], signal[i + 1]);

    float score = 0.5f * fabs(g) + 0.3f * sharp + 0.2f * ncr;

    EdgeDirection dir = (g > 0) ? EdgeDirection::POSITIVE : EdgeDirection::NEGATIVE;
    return { i + offset, g, sharp, ncr, score, dir };
}

// 자동 CD 측정
void autoCDMeasure(const Mat& gray, int row) {
    vector<float> profile(gray.cols);
    for (int x = 0; x < gray.cols; ++x)
        profile[x] = static_cast<float>(gray.at<uchar>(row, x));

    // Gradient (중앙차분 기반)
    vector<EdgePoint> edgePts;
    for (int i = 2; i < profile.size() - 2; ++i) {
        float g_prev = profile[i - 1];
        float g = profile[i];
        float g_next = profile[i + 1];

        // 극값 조건
        if ((g > g_prev && g > g_next) || (g < g_prev && g < g_next)) {
            edgePts.push_back(makeEdgePoint(profile, i));
        }
    }

    // 양/음 edge 분리
    vector<EdgePoint> posEdges, negEdges;
    for (const auto& e : edgePts) {
        if (e.direction == EdgeDirection::POSITIVE)
            posEdges.push_back(e);
        else
            negEdges.push_back(e);
    }

    if (posEdges.empty() || negEdges.empty()) {
        cout << "No valid edge pair found." << endl;
        return;
    }

    // 최고 점수 쌍 찾기
    float bestScore = -1.0f;
    float bestCD = -1.0f;
    EdgePoint bestPos, bestNeg;

    for (const auto& pos : posEdges) {
        for (const auto& neg : negEdges) {
            if (neg.position <= pos.position) continue;
            float pairScore = pos.score * neg.score;
            if (pairScore > bestScore) {
                bestScore = pairScore;
                bestCD = neg.position - pos.position;
                bestPos = pos;
                bestNeg = neg;
            }
        }
    }

    // 결과 출력
    cout << "Best CD = " << bestCD << " pixels" << endl;
    cout << " → PosEdge x=" << bestPos.position << ", Score=" << bestPos.score << endl;
    cout << " → NegEdge x=" << bestNeg.position << ", Score=" << bestNeg.score << endl;

    // 시각화
    Mat vis;
    cvtColor(gray, vis, COLOR_GRAY2BGR);
    circle(vis, Point((int)bestPos.position, row), 3, Scalar(0, 255, 0), -1);
    circle(vis, Point((int)bestNeg.position, row), 3, Scalar(0, 0, 255), -1);
    imshow("Auto CD Measure", vis);
    waitKey(0);
}

int main(int argc, char** argv) {
    if (argc < 2) {
        cout << "Usage: ./CDMeasure <image_path>" << endl;
        return -1;
    }

    Mat img = imread(argv[1], IMREAD_GRAYSCALE);
    if (img.empty()) {
        cerr << "Image load failed!" << endl;
        return -1;
    }

    int rowToUse = img.rows / 2;
    autoCDMeasure(img, rowToUse);
    return 0;
}

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