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