일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- edge
- c++
- Gaussian
- sensor
- subpixel
- mfc
- Gradient
- parameter
- Binary
- file access
- Unity
- public
- APP
- UNO
- Android
- Pointer
- Class
- atmega328
- Contour
- flutter
- compare
- Filtering
- aduino
- wpf
- Encapusulation
- digitalRead
- memory
- SERIAL
- Read
- stream
- Today
- Total
폴크(FOLC)
치수 계산하기 - subpixel - fitting 본문
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;
}