엔진은 카메라도 화면도 모릅니다. 프레임 버퍼를 받아 결과 구조체를 돌려줄 뿐입니다. 호스트 애플리케이션이 그 사이를 연결합니다.
#include "defect_engine.h"
/* 1. 시작할 때 한 번 */
SemDefectConfig config;
sem_defect_default_config(&config);
config.fail_ratio_percent = 15.0f; /* 필요하면 조정 */
if (sem_defect_init("/path/to/model.tflite", &config) != 0) {
return -1;
}
/* 2. 프레임마다 */
int defect_pixels = sem_defect_run(frame_rgb, frame_width, frame_height);
if (defect_pixels < 0) {
fprintf(stderr, "inference failed\n");
}
/* 3. 결과가 필요한 곳에서 (다른 스레드여도 됨) */
SemDefectResult result;
if (sem_defect_get_latest_result(&result) == 0) {
draw_verdict(result.is_fail ? "FAIL" : "PASS");
draw_class(result.predicted_class,
result.class_probabilities[result.predicted_class - 1]);
}
/* 4. 종료할 때 */
sem_defect_destroy();sem_defect_run() 은 interleaved RGB8 을 받습니다.
byte 0 1 2 3 4 5 ...
R0 G0 B0 R1 G1 B1 ...
카메라가 다른 형식을 내보내면 호출 전에 변환해야 합니다.
일부 캡처 드라이버는 planar(RRR...GGG...BBB...)로 내보냅니다.
프레임마다 변환이 들어가므로 SIMD를 쓰는 편이 좋습니다.
/* 채널별 시작 주소 */
const uint8_t *r = buffer;
const uint8_t *g = buffer + (width * height);
const uint8_t *b = buffer + (width * height * 2);
rgb_planar_to_interleaved(r, g, b, interleaved, width, height);
sem_defect_run(interleaved, width, height);cv::cvtColor(..., cv::COLOR_BGR2RGB) 로 변환하거나,
엔진의 cv::COLOR_RGB2GRAY 를 COLOR_BGR2GRAY 로 바꿉니다.
그레이스케일 변환 계수가 채널마다 다르므로(R 0.299 / G 0.587 / B 0.114)
RGB와 BGR을 섞으면 결과가 달라집니다.
엔진은 프레임 버퍼를 복사하지 않고 cv::Mat 으로 감쌉니다.
대신 sem_defect_run() 이 반환한 뒤에는 그 버퍼를 참조하지 않으므로,
호출 직후 캡처 버퍼를 반납해도 안전합니다.
/* V4L2 예시 */
dequeue_buffer(fd, &buf);
sem_defect_run(buffers[buf.index].start, width, height);
queue_buffer(fd, buf.index); /* 추론 후 즉시 반납 */현재 sem_defect_run() 은 동기 호출이라 호출한 스레드를 막습니다.
표시 루프에서 직접 부르면 추론 지연시간이 프레임레이트 상한이 됩니다.
권장 구조:
[캡처 스레드] --frame--> [추론 스레드] --result--> [표시 루프]
(최신 값만 읽음)
표시 루프는 매 프레임 sem_defect_get_latest_result() 를 호출해 가장 최근에
완료된 결과를 그립니다. 추론이 표시보다 느려도 화면은 계속 갱신됩니다.
주의.
get_latest_result()는 구조체를 복사해 넘기지만, 현재 구현에는 결과를 보호하는 뮤텍스가 없습니다. 위 구조로 갈 때 엔진 내부에 락을 추가해야 합니다.SemDefectResult는 POD이므로 뮤텍스 보유 구간은memcpy한 번으로 짧게 유지할 수 있습니다.
| 필드 | 의미 |
|---|---|
predicted_class |
1 ~ 6. 데이터셋 라벨과 맞추기 위해 1부터 시작 |
class_probabilities[] |
확률. 모델 내부에서 softmax가 적용된 값 |
probability_sum |
위 값들의 합. 정상이면 ~1.0 |
defect_pixels |
임계값을 넘은 픽셀 수 (최대 470×470 = 220,900) |
defect_ratio |
결함 면적 퍼센트. 0.0 ~ 100.0 |
is_fail |
defect_ratio >= fail_ratio_percent |
latency_ms |
Invoke() 만 측정. 전처리/후처리 제외 |
class_probabilities 에 softmax를 다시 적용하지 마세요. 모델 그래프가
이미 softmax를 포함하고 있어 두 번 적용하면 분포가 평탄해집니다.
확인 방법은 design-notes.md
를 참고하세요.