반응형
250x250
Notice
Recent Posts
Recent Comments
Link
«   2025/07   »
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)

치수 계산하기 - subpixel - fitting 본문

카테고리 없음

치수 계산하기 - subpixel - fitting

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

Edge line 전체에 대해 Least-Squares Line Fitting을 수행하면, 단순히 픽셀마다 edge를 추정하는 것이 아니라, 전체 edge 점들을 직선 또는 곡선으로 모델링하여 더 안정적이고 subpixel 수준의 CD(임계 치수) 측정이 가능하다.

개념

- 입력 : edge image / binary edge mask image
- 처리 : edge point detect -> line fitting model : ax + by + c = 0
- 출력 : line to line distance, line equation of each line

 

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;

// 직선 피팅 함수 (OpenCV fitLine 사용)
Vec4f fitEdgeLine(const vector<Point>& edgePoints) {
    Vec4f line;
    fitLine(edgePoints, line, DIST_L2, 0, 0.01, 0.01);
    return line;  // (vx, vy, x0, y0)
}

// 두 직선 간 평균 수직 거리 측정
float measureLineToLineDistance(const Vec4f& line1, const Vec4f& line2) {
    // 두 직선 위의 점
    Point2f p1(line1[2], line1[3]);
    Point2f p2(line2[2], line2[3]);

    // line1의 단위 방향벡터 수직 방향
    Point2f normal(-line1[1], line1[0]);

    Point2f diff = p2 - p1;
    float dist = abs(diff.dot(normal));  // 수직 거리
    return dist;
}

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

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

    // 전처리 및 Canny edge
    Mat edges;
    Canny(src, edges, 50, 150);

    // 두 개 edge line의 점 추출 (수동 예시: 왼쪽/오른쪽 ROI로 분리)
    vector<Point> leftEdgePts, rightEdgePts;
    int midX = src.cols / 2;

    for (int y = 0; y < edges.rows; ++y) {
        for (int x = 0; x < edges.cols; ++x) {
            if (edges.at<uchar>(y, x) > 0) {
                if (x < midX)
                    leftEdgePts.emplace_back(x, y);
                else
                    rightEdgePts.emplace_back(x, y);
            }
        }
    }

    if (leftEdgePts.empty() || rightEdgePts.empty()) {
        cout << "Edge lines not found." << endl;
        return -1;
    }

    // 각 edge 라인에 대해 직선 피팅
    Vec4f leftLine = fitEdgeLine(leftEdgePts);
    Vec4f rightLine = fitEdgeLine(rightEdgePts);

    // 수직 거리 측정
    float cd = measureLineToLineDistance(leftLine, rightLine);
    cout << "Fitted Critical Dimension: " << cd << " pixels" << endl;

    // 시각화
    Mat vis;
    cvtColor(src, vis, COLOR_GRAY2BGR);

    Point2f leftP(leftLine[2], leftLine[3]);
    Point2f leftDir(leftLine[0], leftLine[1]);
    line(vis, leftP - 1000 * leftDir, leftP + 1000 * leftDir, Scalar(0, 255, 0), 1);

    Point2f rightP(rightLine[2], rightLine[3]);
    Point2f rightDir(rightLine[0], rightLine[1]);
    line(vis, rightP - 1000 * rightDir, rightP + 1000 * rightDir, Scalar(0, 0, 255), 1);

    imshow("Fitted Edge Lines", vis);
    waitKey(0);

    return 0;
}

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