AI/ML Uç Bilişim
TEKNİK REHBER AI/ML UÇ BİLİŞİM OpenCV Gömülü 2026

OpenCV Gömülü —
NEON · GStreamer pipeline.

aarch64 cross-compile, NEON intrinsic hızlandırması, V4L2 kamera pipeline ve GStreamer entegrasyonu. ARM Mali GPU OpenCL backend. Conveyor belt hata tespiti pratik senaryosu.

00 OpenCV gömülü kullanım senaryoları

OpenCV, dünyada en yaygın kullanılan açık kaynak bilgisayarlı görme kütüphanesidir. Gömülü Linux sistemlerde, AI çıkarımı öncesinde ya da bağımsız görüntü işleme pipeline'larında vazgeçilmez bir araçtır.

Gömülü OpenCV senaryoları

AI ön işleme
TFLite/ONNX modellerine giriş için görüntü yeniden boyutlandırma, renk dönüşümü, normalizasyon ve augmentation pipeline'ı. Model çıkarımından önce zorunlu adımlar.
Kural tabanlı görüntü işleme
Endüstriyel hata tespiti, boyut ölçümü, renk sınıflandırma gibi deterministik algoritmalar. Basit senaryolarda AI gerektirmez; daha hızlı ve yorumlanabilirdir.
Kamera pipeline yönetimi
V4L2, GStreamer veya MIPI CSI kamera verilerini OpenCV frame'lerine dönüştürme. Format dönüşümü (NV12, YUV420 → BGR), demosaic, lens distorsiyon düzeltmesi.
AI son işleme
Model çıktısı üzerinde NMS, tracking (SORT, DeepSORT), pose visualization, segmentation mask render. GPU hızlandırması ile birleştirilebilir.

Alternatifler ve ne zaman OpenCV kullanılır?

AraçGüçlü YönlerZayıf YönlerTercih Edildiği Durum
OpenCVGeniş op kümesi, iyi belgelenmişBüyük binary (~30MB)Genel amaçlı görüntü işleme
libjpeg/libpngÇok küçük, minimalYalnızca codecMCU, çok kısıtlı bellek
Pillow (Python)Kolay APIC/C++ yok, yavaşPrototipleme, Python script
HalideOtomatik optimizasyonÖğrenme eğrisi yüksekPipeline optimizasyonu araştırma
Vitis AIXilinx FPGA hızlandırmaXilinx'e özgüFPGA tabanlı gömülü

01 Cross-compile — aarch64 toolchain, CMake

Host PC'de (x86_64 Ubuntu) aarch64 için OpenCV derlemek, hedef cihazda derlemeye göre çok daha hızlıdır. Minimal modül seçimi ve headless build, binary boyutunu azaltır.

Toolchain kurulumu

bash — Ubuntu 22.04 host
sudo apt-get install -y \
    gcc-aarch64-linux-gnu \
    g++-aarch64-linux-gnu \
    pkg-config cmake ninja-build

# Sysroot hazırlama (RPi4 OS dosyalarını host'a kopyala)
mkdir -p /opt/sysroot-rpi4
rsync -avz --rsync-path="sudo rsync" \
      pi@raspberrypi.local:/lib  /opt/sysroot-rpi4/
rsync -avz --rsync-path="sudo rsync" \
      pi@raspberrypi.local:/usr/lib  /opt/sysroot-rpi4/usr/
rsync -avz --rsync-path="sudo rsync" \
      pi@raspberrypi.local:/usr/include  /opt/sysroot-rpi4/usr/

CMake toolchain dosyası

aarch64-rpi4.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)

set(TOOLCHAIN_PREFIX aarch64-linux-gnu)
set(CMAKE_C_COMPILER   ${TOOLCHAIN_PREFIX}-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++)
set(CMAKE_STRIP        ${TOOLCHAIN_PREFIX}-strip)

set(SYSROOT /opt/sysroot-rpi4)
set(CMAKE_SYSROOT ${SYSROOT})

set(CMAKE_FIND_ROOT_PATH ${SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

# ARM NEON ve Cortex-A72 optimizasyonu
set(CMAKE_C_FLAGS   "-march=armv8-a+simd -O3 -ffast-math")
set(CMAKE_CXX_FLAGS "-march=armv8-a+simd -O3 -ffast-math")

OpenCV minimal CMake build

bash — OpenCV cross-compile
git clone --depth 1 --branch 4.9.0 \
    https://github.com/opencv/opencv
mkdir opencv-build-aarch64 && cd opencv-build-aarch64

cmake ../opencv \
    -DCMAKE_TOOLCHAIN_FILE=../aarch64-rpi4.cmake \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX=/opt/opencv-aarch64 \
    \
    # NEON hızlandırma
    -DENABLE_NEON=ON \
    -DWITH_OPENCL=ON \
    \
    # Headless build: GUI yok
    -DWITH_GTK=OFF \
    -DWITH_QT=OFF \
    -DWITH_OPENGL=OFF \
    \
    # Kamera
    -DWITH_V4L=ON \
    -DWITH_GSTREAMER=ON \
    -DWITH_FFMPEG=OFF \
    \
    # Minimal modüller: sadece gerekli olanlar
    -DBUILD_LIST=core,imgproc,imgcodecs,videoio,video \
    \
    # Python binding'leri devre dışı
    -DBUILD_opencv_python2=OFF \
    -DBUILD_opencv_python3=OFF \
    -DBUILD_TESTS=OFF \
    -DBUILD_PERF_TESTS=OFF \
    -DBUILD_EXAMPLES=OFF

make -j$(nproc)
make install

# Sonuç: /opt/opencv-aarch64/lib/libopencv_*.so (~15MB toplam)
ls -lh /opt/opencv-aarch64/lib/libopencv_*.so
Modül Seçimi

-DBUILD_LIST ile yalnızca gerekli modülleri derleyerek binary boyutunu 30MB'tan 5-10MB'a düşürebilirsin. Nesne tespiti için: core, imgproc, imgcodecs, videoio. DNN çıkarımı için dnn modülünü ekle.

02 NEON optimizasyonu — ENABLE_NEON, Universal Intrinsics

ARM NEON, aarch64'te 128-bit SIMD işlemleri sağlar. OpenCV, NEON'u Universal Intrinsics (UI) soyutlama katmanı üzerinden kullanır; aynı kod AVX2, NEON ve RVV'ye derlenir.

NEON kapasite kontrolü

neon_check.cpp
#include <opencv2/core.hpp>
#include <iostream>

int main() {
    // OpenCV CPU özelliklerini listele
    std::string features = cv::getBuildInformation();
    std::cout << features << std::endl;

    // NEON aktif mi?
    bool neon = cv::checkHardwareSupport(CV_CPU_NEON);
    std::cout << "NEON: " << (neon ? "EVET" : "HAYIR") << std::endl;

    // FP16 NEON
    bool fp16 = cv::checkHardwareSupport(CV_CPU_FP16);
    std::cout << "FP16: " << (fp16 ? "EVET" : "HAYIR") << std::endl;
    return 0;
}

Universal Intrinsics (UI) ile özel kernel

neon_normalize.cpp
#include <opencv2/core/hal/intrin.hpp>
#include <opencv2/imgproc.hpp>

// NEON hızlandırmalı uint8 → float normalize
// [0,255] → [0.0, 1.0] dönüşümü, 16 piksel paralel
void normalize_neon(const uint8_t* src, float* dst, int n) {
    const float inv255 = 1.0f / 255.0f;
    int i = 0;

    // 16 piksel blok halinde işle (NEON 128-bit = 16x uint8)
    for (; i <= n - 16; i += 16) {
        cv::v_uint8x16 v_src = cv::v_load(src + i);

        // uint8 → uint16 genişlet (2 blok × 8)
        cv::v_uint16x8 lo, hi;
        cv::v_expand(v_src, lo, hi);

        // uint16 → float32 (4 blok × 4)
        cv::v_float32x4 f0, f1, f2, f3;
        cv::v_expand(lo, f0, f1);   // uygun tip dönüşümü
        // ... (tam implementasyon için v_cvt_f32 kullanılır)

        // Scale: × (1/255)
        cv::v_float32x4 scale = cv::v_setall_f32(inv255);
        f0 = f0 * scale;
        f1 = f1 * scale;

        cv::v_store(dst + i,     f0);
        cv::v_store(dst + i + 4, f1);
    }

    // Kalan pikseller
    for (; i < n; i++)
        dst[i] = src[i] * inv255;
}

// OpenCV önerilen yol: convertTo (NEON otomatik kullanılır)
void normalize_opencv(const cv::Mat& src, cv::Mat& dst) {
    src.convertTo(dst, CV_32F, 1.0/255.0);  // NEON otomatik
}

NEON benchmark

İşlemC skalerNEON (auto)NEON (manual)
convertTo float32 (1080p)28 ms7 ms6 ms
resize INTER_LINEAR (1080→640)22 ms6 ms
GaussianBlur 5x5 (1080p)45 ms11 ms
BGR2RGB (1080p)8 ms2 ms1.5 ms

03 Kamera pipeline — V4L2, VideoCapture, normalize

Linux'ta kamera erişiminin standart yolu V4L2 (Video for Linux 2)'dir. OpenCV'nin VideoCapture sınıfı, V4L2 backend ile USB ve CSI kameralara şeffaf erişim sağlar.

Temel kamera yakalama

camera_capture.cpp
#include <opencv2/videoio.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

int main() {
    // V4L2 backend ile aç (MJPEG için daha hızlı)
    cv::VideoCapture cap("/dev/video0", cv::CAP_V4L2);

    // Format ayarla
    cap.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc('M','J','P','G'));
    cap.set(cv::CAP_PROP_FRAME_WIDTH,  640);
    cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480);
    cap.set(cv::CAP_PROP_FPS, 30);
    cap.set(cv::CAP_PROP_BUFFERSIZE, 2);  // düşük gecikme

    if (!cap.isOpened()) {
        std::cerr << "Kamera açılamadı!" << std::endl;
        return -1;
    }

    cv::Mat frame, rgb_frame, resized, normalized;

    while (true) {
        cap.read(frame);  // BGR, uint8
        if (frame.empty()) break;

        // Renk dönüşümü: BGR → RGB
        cv::cvtColor(frame, rgb_frame, cv::COLOR_BGR2RGB);

        // AI giriş boyutuna yeniden boyutlandır
        cv::resize(rgb_frame, resized, cv::Size(224, 224),
                   0, 0, cv::INTER_LINEAR);

        // Normalizasyon: [0,255] → [0.0, 1.0] float32
        resized.convertTo(normalized, CV_32F, 1.0/255.0);

        // Model girişine kopyala (NCHW format için)
        // cv::dnn::blobFromImage daha kullanışlı:
        cv::Mat blob = cv::dnn::blobFromImage(
            resized,
            1.0/255.0,          // scale
            cv::Size(224, 224), // boyut
            cv::Scalar(0,0,0),  // mean (ImageNet: 0.485,0.456,0.406)
            true,               // swapRB: BGR→RGB
            false               // crop
        );
        // blob.shape: (1, 3, 224, 224) — NCHW, float32
    }

    cap.release();
    return 0;
}

V4L2 format desteği kontrolü

bash
# Desteklenen formatları listele
v4l2-ctl --list-formats-ext -d /dev/video0

# Mevcut kameralar
v4l2-ctl --list-devices

# Kamera özellikleri (brightness, contrast, vs.)
v4l2-ctl --list-ctrls -d /dev/video0

# Exposure ve gain manuel ayar (otomatik kapalı)
v4l2-ctl -d /dev/video0 \
    --set-ctrl=exposure_auto=1 \
    --set-ctrl=exposure_absolute=200

04 GStreamer entegrasyonu — appsink, HW decoder

GStreamer, Linux'ta güçlü bir medya pipeline çerçevesidir. OpenCV'nin VideoCapture sınıfı, GStreamer pipeline dizelerini doğrudan kabul eder; böylece donanım hızlandırmalı decoder ve çoklu kaynak kullanılabilir.

GStreamer ile VideoCapture

gstreamer_capture.cpp
#include <opencv2/videoio.hpp>
#include <string>

// USB kamera — MJPEG donanım decode
std::string gst_usb =
    "v4l2src device=/dev/video0 ! "
    "image/jpeg,width=1280,height=720,framerate=30/1 ! "
    "jpegdec ! "
    "videoconvert ! "
    "video/x-raw,format=BGR ! "
    "appsink max-buffers=2 drop=true sync=false";

// RPi kamera — picamera2 üzerinden (libcamera-src)
std::string gst_picam =
    "libcamerasrc ! "
    "video/x-raw,width=1280,height=720,framerate=30/1 ! "
    "videoconvert ! "
    "video/x-raw,format=BGR ! "
    "appsink max-buffers=2 drop=true";

// NV12 donanım decode (RPi4 V4L2 M2M decoder)
std::string gst_hw_decode =
    "filesrc location=test_720p.h264 ! "
    "h264parse ! "
    "v4l2h264dec ! "          // RPi4 donanım H.264 decoder
    "video/x-raw,format=NV12 ! "
    "videoconvert ! "
    "video/x-raw,format=BGR ! "
    "appsink max-buffers=2 drop=true";

// Ağ kamera (RTSP)
std::string gst_rtsp =
    "rtspsrc location=rtsp://192.168.1.100:554/stream latency=100 ! "
    "rtph264depay ! h264parse ! "
    "avdec_h264 ! "
    "videoconvert ! "
    "video/x-raw,format=BGR ! "
    "appsink max-buffers=2 drop=true";

cv::VideoCapture cap(gst_usb, cv::CAP_GSTREAMER);
cv::Mat frame;
while (cap.read(frame)) {
    // frame: BGR, uint8, 1280×720
}

NV12 → BGR dönüşümü (NEON)

nv12_to_bgr.cpp
// GStreamer appsink'ten gelen NV12 frame'i BGR'ye çevir
// NV12: Y plane (W×H) + UV interleaved plane (W×H/2)
void nv12_to_bgr(const uint8_t* nv12_data,
                 int width, int height,
                 cv::Mat& bgr_out) {
    // OpenCV'nin YUV2BGR fonksiyonu NV12 (YUV420sp) destekler
    cv::Mat yuv(height + height/2, width, CV_8UC1,
                (void*)nv12_data);
    cv::cvtColor(yuv, bgr_out, cv::COLOR_YUV2BGR_NV12);
    // NEON ile otomatik hızlandırılmış
}

05 OpenCL/GPU hızlandırma — Mali, UMat, profiling

OpenCV'nin T-API (Transparent API), cv::UMat üzerinden OpenCL backend'i şeffaf biçimde kullanır. ARM Mali GPU'lu RPi 4 veya RK3588'de belirli işlemler için belirgin hızlanma sağlar.

UMat kullanımı

opencl_umat.cpp
#include <opencv2/core/ocl.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

int main() {
    // OpenCL mevcut mu?
    if (!cv::ocl::haveOpenCL()) {
        std::cout << "OpenCL yok, CPU kullanılıyor" << std::endl;
    } else {
        cv::ocl::setUseOpenCL(true);
        cv::ocl::Context ctx = cv::ocl::Context::getDefault();
        std::cout << "OpenCL cihaz: "
                  << ctx.device(0).name() << std::endl;
        // Örnek: Mali-G52 MP2
    }

    cv::Mat cpu_frame = cv::imread("test.jpg");

    // Mat → UMat (GPU belleğine kopyala)
    cv::UMat gpu_frame;
    cpu_frame.copyTo(gpu_frame);

    // Aşağıdaki işlemler GPU'da çalışır (OpenCL):
    cv::UMat gray, blurred, resized;
    cv::cvtColor(gpu_frame, gray,    cv::COLOR_BGR2GRAY);
    cv::GaussianBlur(gray, blurred,  cv::Size(5,5), 1.5);
    cv::resize(blurred, resized,     cv::Size(640,640));

    // Sonucu CPU'ya geri al
    cv::Mat result;
    resized.copyTo(result);

    return 0;
}

OpenCL profiling

bash — profiling
# OpenCL kernel profilini etkinleştir
export OPENCV_OCL_PROFILING=1

# Uygulama çalıştır
./opencv_app 2>&1 | grep -E "kernel|time"

# Mali GPU kullanım izleme (Panfrost sürücü)
cat /sys/bus/platform/drivers/panfrost/*/utilization

# opencl benchmark
clpeak  # opencl peak performance testi
UMat Kopyalama Maliyeti

UMat'ın faydası yalnızca hesaplama yoğun işlemlerde ortaya çıkar. Küçük görüntüler veya basit işlemler için CPU→GPU ve GPU→CPU kopyalama maliyeti, hesaplama kazancını geçebilir. Profiling ile karar ver.

06 Temel görüntü işleme — threshold, contour, Kalman

Gömülü görüntü işleme pipeline'larında sıkça kullanılan algoritmalar: adaptif eşikleme, morfoloji, kontur analizi ve Kalman filtresi ile nesne takibi.

Adaptif eşikleme ve morfoloji

defect_detect.cpp
#include <opencv2/imgproc.hpp>
#include <vector>

struct Defect {
    cv::Rect   bbox;
    double     area;
    cv::Point2f center;
};

std::vector<Defect> find_defects(const cv::Mat& frame) {
    cv::Mat gray, binary, morph;

    // Gri ton
    cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);

    // Adaptif eşikleme (aydınlatma değişimine dayanıklı)
    cv::adaptiveThreshold(
        gray, binary,
        255,
        cv::ADAPTIVE_THRESH_GAUSSIAN_C,
        cv::THRESH_BINARY_INV,
        31,    // block size
        5      // sabit C
    );

    // Morfoloji: gürültüyü kaldır, hataları koru
    cv::Mat kernel = cv::getStructuringElement(
        cv::MORPH_ELLIPSE, cv::Size(5,5));
    cv::morphologyEx(binary, morph, cv::MORPH_CLOSE, kernel);
    cv::morphologyEx(morph,  morph, cv::MORPH_OPEN,  kernel);

    // Kontur tespiti
    std::vector<std::vector<cv::Point>> contours;
    cv::findContours(morph, contours,
                     cv::RETR_EXTERNAL,
                     cv::CHAIN_APPROX_SIMPLE);

    // Hata filtresi: alan eşiği
    std::vector<Defect> defects;
    for (auto& c : contours) {
        double area = cv::contourArea(c);
        if (area > 50 && area < 10000) {  // piksel² cinsinden
            Defect d;
            d.area   = area;
            d.bbox   = cv::boundingRect(c);
            cv::Moments M = cv::moments(c);
            d.center = cv::Point2f(M.m10/M.m00, M.m01/M.m00);
            defects.push_back(d);
        }
    }
    return defects;
}

Kalman filtresi ile nesne takibi

kalman_tracker.cpp
#include <opencv2/video/tracking.hpp>

class DefectTracker {
public:
    cv::KalmanFilter kf;
    cv::Mat state;     // [x, y, vx, vy]
    cv::Mat meas;      // [x, y]
    bool initialized = false;

    DefectTracker() {
        kf.init(4, 2, 0, CV_32F);
        // Durum geçiş matrisi: konum + hız modeli
        cv::setIdentity(kf.transitionMatrix);
        kf.transitionMatrix.at<float>(0,2) = 1; // x += vx*dt
        kf.transitionMatrix.at<float>(1,3) = 1; // y += vy*dt

        // Ölçüm matrisi: sadece konum gözlemlenir
        kf.measurementMatrix = cv::Mat::zeros(2, 4, CV_32F);
        kf.measurementMatrix.at<float>(0,0) = 1;
        kf.measurementMatrix.at<float>(1,1) = 1;

        cv::setIdentity(kf.processNoiseCov,      cv::Scalar(1e-4));
        cv::setIdentity(kf.measurementNoiseCov,  cv::Scalar(1e-1));
        cv::setIdentity(kf.errorCovPost,         cv::Scalar(1));

        meas = cv::Mat::zeros(2, 1, CV_32F);
    }

    cv::Point2f predict() {
        cv::Mat prediction = kf.predict();
        return cv::Point2f(prediction.at<float>(0),
                           prediction.at<float>(1));
    }

    cv::Point2f update(cv::Point2f detected) {
        meas.at<float>(0) = detected.x;
        meas.at<float>(1) = detected.y;
        cv::Mat corrected = kf.correct(meas);
        return cv::Point2f(corrected.at<float>(0),
                           corrected.at<float>(1));
    }
};

07 Pratik — Conveyor belt hata tespiti

Endüstriyel konveyör bant üzerinde ürün hata tespiti. USB kamera, Raspberry Pi 4, OpenCV pipeline ve çıkış için seri port alarmı. Hedef: <20ms işleme süresi.

Sistem mimarisi

  USB Kamera (640×480, 30fps)
       ↓ MJPEG → BGR
  [GStreamer/V4L2 capture]
       ↓
  [ROI kırp — bant alanı]
       ↓
  [Beyazlatma + gri ton]
       ↓
  [Adaptif threshold]
       ↓
  [Morfoloji — açma/kapama]
       ↓
  [Kontur analizi — alan filtresi]
       ↓
  [Kalman takip — sahte alarm azalt]
       ↓
  ┌────────────────────────────────┐
  │ HATA var?                      │
  │  Evet → GPIO HIGH + UART alarm │
  │  Hayır → devam                 │
  └────────────────────────────────┘
    

Tam implementasyon

conveyor_detect.py
import cv2, numpy as np, serial, time
import RPi.GPIO as GPIO

# --- Konfigürasyon ---
ALARM_PIN    = 17
ROI          = (100, 50, 440, 380)   # (x1, y1, x2, y2)
MIN_AREA     = 200
ALARM_FRAMES = 3   # Kaç ardışık frame'de hata olursa alarm verilir

# --- UART Alarm ---
ser = serial.Serial("/dev/ttyS0", 115200, timeout=0.1)

# --- GPIO ---
GPIO.setmode(GPIO.BCM)
GPIO.setup(ALARM_PIN, GPIO.OUT, initial=GPIO.LOW)

# --- Kamera (GStreamer MJPEG) ---
gst = ("v4l2src device=/dev/video0 ! "
       "image/jpeg,width=640,height=480,framerate=30/1 ! "
       "jpegdec ! videoconvert ! video/x-raw,format=BGR ! "
       "appsink max-buffers=2 drop=true")
cap = cv2.VideoCapture(gst, cv2.CAP_GSTREAMER)

kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7,7))
alarm_counter  = 0
total_defects  = 0
frame_count    = 0

def send_alarm(defect_count, area):
    msg = f"ALARM:defects={defect_count},area={area:.0f}\r\n"
    ser.write(msg.encode())
    GPIO.output(ALARM_PIN, GPIO.HIGH)

while True:
    ret, frame = cap.read()
    if not ret: break
    frame_count += 1

    x1, y1, x2, y2 = ROI
    roi = frame[y1:y2, x1:x2]

    # İşleme pipeline
    gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
    eq   = cv2.equalizeHist(gray)                         # aydınlatma normalleştirme
    blur = cv2.GaussianBlur(eq, (5,5), 0)
    thr  = cv2.adaptiveThreshold(blur, 255,
               cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
               cv2.THRESH_BINARY_INV, 21, 4)
    morph = cv2.morphologyEx(thr,  cv2.MORPH_CLOSE, kernel)
    morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN,  kernel)

    contours, _ = cv2.findContours(morph,
                      cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    defects = [c for c in contours
               if MIN_AREA < cv2.contourArea(c) < 15000]

    # Alarm mantığı
    if defects:
        alarm_counter += 1
        if alarm_counter >= ALARM_FRAMES:
            total_area = sum(cv2.contourArea(c) for c in defects)
            send_alarm(len(defects), total_area)
            total_defects += len(defects)
            # Bounding box çiz
            for c in defects:
                x,y,w,h = cv2.boundingRect(c)
                cv2.rectangle(roi, (x,y), (x+w,y+h), (0,0,255), 2)
    else:
        alarm_counter = 0
        GPIO.output(ALARM_PIN, GPIO.LOW)

    # Overlay
    status = f"HATA: {len(defects)}" if defects else "TAMAM"
    color  = (0,0,255) if defects else (0,255,0)
    cv2.putText(frame, status, (10,30),
                cv2.FONT_HERSHEY_SIMPLEX, 1.0, color, 2)

    if frame_count % 300 == 0:
        print(f"Toplam frame: {frame_count}, "
              f"Tespit edilen hata: {total_defects}")

Performans ölçümü (RPi4)

AdımSüreOptimizasyon
Kamera capture4 msGStreamer buffer
Gri ton + equalize2 msNEON
Adaptive threshold6 msNEON
Morfoloji (2x)3 msNEON
Kontur analizi1.5 ms
Toplam16.5 ms<20ms hedefi OK
Üretim İpuçları

Aydınlatmayı sabit tut (strobo LED, tetiklenmiş flaş) — değişken ortam ışığı en büyük doğruluk düşmanıdır. ROI kırpma işlem süresini %60 azaltır. Alarm debounce (ardışık ALARM_FRAMES) sahte pozitif sayısını büyük ölçüde azaltır.