Setelah format data teks di Chapter sebelumnya, kita berpindah ke data yang sama sekali berbeda: gambar — jutaan angka kecil yang kebetulan terlihat sebagai dunia. Computer vision adalah wilayah yang sejak awal didominasi C++ karena kebutuhan pemrosesan piksel yang masif, dan library yang menaunginya adalah OpenCV: lebih dari 2.500 algoritma yang mencakup pembacaan citra, transformasi warna, deteksi objek, pelacakan video, hingga modul deep learning. OpenCV tidak terinstal di lingkungan course ini, sehingga pola pemakaiannya kita tampilkan sebagai kode untuk dijalankan di mesin Anda — tetapi konsep intinya, matriks piksel dan konversi warna, kita buktikan langsung dengan program yang memproses buffer gambar sungguhan.

Apa yang Ditawarkan OpenCV #

OpenCV disusun per modul: core (struktur data dasar), imgproc (filter, transformasi geometri, histogram), video (analisis gerak), features2d (penandan ciri seperti ORB/SIFT), objdetect (deteksi wajah, QR code), ml (machine learning klasik), dan dnn (menjalankan model neural network). C++ adalah bahasa utamanya — Python binding yang populer hanyalah kulit di atas implementasi C++ yang sama — sehingga mempelajari API C++ berarti membaca sumber kebenarannya langsung.

cv::Mat: Struktur Data Inti #

Semua algoritma OpenCV bekerja di atas cv::Mat — matriks N-dimensi yang menyimpan: sebuah header kecil (dimensi, jumlah channel, tipe data, ukuran step per baris) dan pointer ke buffer data piksel yang besar. Pemisahan ini penting: menyalin Mat atau mengambil potongannya tidak menyalin piksel — object baru hanya berbagi buffer yang sama dengan counter referensi. Pola ini membuat operasi seperti memotong area (ROI) atau mengubah perspektif jadi murah, tetapi juga berarti mengubah potongan bisa diam-diam mengubah gambar aslinya.

Tipe piksel ditulis CV_<bit><tipe>C<channel>: gambar foto umumnya CV_8UC3 — 8-bit unsigned, 3 channel per piksel. Perhatikan urutan channel-nya:

Operasi Dasar yang Paling Sering Dipakai #

cpp Pola cv::Mat (ilustrasi)
#include <opencv2/opencv.hpp>

cv::Mat warna = cv::imread("potret.jpg");        // muat berkas; kosong bila gagal
if (warna.empty()) { /* tangani kesalahan */ }

cv::Mat abu;
cv::cvtColor(warna, abu, cv::COLOR_BGR2GRAY);    // RGB -> grayscale

cv::Rect kotak(40, 20, 100, 100);                // ROI: tanpa menyalin piksel
cv::Mat wajah = warna(kotak);                    // berbagi buffer dengan warna

cv::Vec3b piksel = warna.at<cv::Vec3b>(10, 5);   // baris 10, kolom 5
unsigned char biru = piksel[0];                  // urutan channel: B, G, R

cv::imwrite("abu.png", abu);                     // simpan hasil

Instalasinya mengikuti platform Anda:

bash Cara memasang OpenCV 4.x
sudo apt install libopencv-dev     # Debian/Ubuntu
brew install opencv                # macOS
vcpkg install opencv4              # Windows / lintas platform via vcpkg

Untuk mengompilasi program yang memakainya, jalur include dan daftar library yang panjang biasanya diambil dari pkg-config:

bash Cara mengompilasi program OpenCV
g++ -std=c++23 -Wall -Wextra program.cpp $(pkg-config --cflags --libs opencv4) -o program

Konsep yang Sama Tanpa OpenCV: Grayscale Manual #

Di balik satu panggilan cv::cvtColor tersedia kerja yang bisa kita lihat utuh: buffer 3 byte per piksel disusun baris-per-baris, dan tiap piksel dipetakan ke satu angka kecerahan dengan rumus luminance ITU-R BT.601 — 0,299 untuk merah, 0,587 untuk hijau, 0,114 untuk biru (mata manusia paling peka hijau). Program berikut melakukan persis itu pada buffer polos std::vector<unsigned char>, tanpa OpenCV:

cpp
// grayscale-manual.cpp — konversi RGB ke grayscale pada buffer piksel polos.
// Operasinya identik dengan cv::cvtColor(CV_BGR2GRAY) yang bekerja di atas
// cv::Mat: data piksel 8-bit disusun baris-per-baris dalam satu vector.
#include <cstdio>
#include <vector>

struct Gambar {
    int lebar = 0;
    int tinggi = 0;
    std::vector<unsigned char> piksel;  // 3 byte per piksel: R, G, B

    Gambar(int l, int t) : lebar(l), tinggi(t), piksel(l * t * 3) {}

    void setRgb(int x, int y, unsigned char r, unsigned char g, unsigned char b) {
        size_t indeks = (static_cast<size_t>(y) * lebar + x) * 3;
        piksel[indeks]     = r;
        piksel[indeks + 1] = g;
        piksel[indeks + 2] = b;
    }
};

// Rumus luminance ITU-R BT.601, sama yang dipakai konversi grayscale klasik;
// +0.5 sebelum dipangkas membulatkan ke bilangan bulat terdekat.
unsigned char keGrayscale(unsigned char r, unsigned char g, unsigned char b) {
    return static_cast<unsigned char>(0.299 * r + 0.587 * g + 0.114 * b + 0.5);
}

int main() {
    Gambar gbr(4, 3);
    // Baris 0: warna murni dan putih
    gbr.setRgb(0, 0, 255,   0,   0);  // merah
    gbr.setRgb(1, 0,   0, 255,   0);  // hijau
    gbr.setRgb(2, 0,   0,   0, 255);  // biru
    gbr.setRgb(3, 0, 255, 255, 255);  // putih
    // Baris 1: abu-abu dan warna campuran
    gbr.setRgb(0, 1, 128, 128, 128);  // abu-abu
    gbr.setRgb(1, 1, 255, 255,   0);  // kuning
    gbr.setRgb(2, 1,   0, 255, 255);  // cyan
    gbr.setRgb(3, 1, 255,   0, 255);  // magenta
    // Baris 2: kombinasi gelap-acak
    gbr.setRgb(0, 2,  10,  20,  30);
    gbr.setRgb(1, 2, 200, 100,  50);
    gbr.setRgb(2, 2,  50,  50,  50);
    gbr.setRgb(3, 2,   0,   0,   0);

    std::vector<unsigned char> abu(static_cast<size_t>(gbr.lebar) * gbr.tinggi);
    for (int y = 0; y < gbr.tinggi; ++y) {
        for (int x = 0; x < gbr.lebar; ++x) {
            size_t idx = (static_cast<size_t>(y) * gbr.lebar + x) * 3;
            abu[static_cast<size_t>(y) * gbr.lebar + x] =
                keGrayscale(gbr.piksel[idx], gbr.piksel[idx + 1], gbr.piksel[idx + 2]);
        }
    }

    std::printf("Matriks grayscale %dx%d:\n", gbr.lebar, gbr.tinggi);
    for (int y = 0; y < gbr.tinggi; ++y) {
        const char* pemisah = "";
        for (int x = 0; x < gbr.lebar; ++x) {
            std::printf("%s%3d", pemisah, abu[static_cast<size_t>(y) * gbr.lebar + x]);
            pemisah = "  ";
        }
        std::printf("\n");
    }
    return 0;
}

Output yang Dihasilkan:

text
Matriks grayscale 4x3:
 76  150   29  255
128  226  179  105
 18  124   50    0

Bedah bagian pentingnya:

  1. Baris 7-20 (struct Gambar) — buffer std::vector<unsigned char> dengan panjang lebar * tinggi * 3 — inilah penataan yang sama seperti data di dalam cv::Mat berformat CV_8UC3, disimpan baris-per-baris (row-major).
  2. Baris 14-19 (setRgb) — rumus alamat piksel: (y * lebar + x) * 3. Menguasai formula ini berarti bisa membaca/menulis piksel di buffer apa pun, termasuk Mat::data yang tipe mentahnya unsigned char*.
  3. Baris 24-26 (keGrayscale) — kombinasi linear tiga channel; koefisiennya berjumlah tepat 1,0 sehingga warna abu-abu (128,128,128) tetap bernilai 128. Tambahan + 0.5 sebelum dipangkas membulatkan ke bilangan terdekat, bukan selalu ke bawah.
  4. Baris 29-44 (pengisian pola) — dua belas piksel uji yang dipilih agar hasilnya bisa diperiksa manual: merah murni menjadi 76 (0,299 × 255), hijau menjadi 150, biru menjadi 29, dan putih 255.
  5. Baris 46-53 (loop konversi) — persis pekerjaan cv::cvtColor(COLOR_BGR2GRAY) untuk kasus tanpa step padding: membaca tiga byte, menulis satu byte, per piksel.

Cocokkan hasilnya dengan tabel: (128,128,128) → 128 dan (255,255,0) kuning → 226 (0,299 + 0,587 kali 255). Bila nanti Anda memanggil cv::cvtColor, Anda tahu persis apa yang dihitungnya — dan bila profiler menunjukkan konversi warna sebagai hambatan, Anda tahu cara menulis versi manualnya.

Kapan OpenCV Tepat #

Gunakan OpenCV begitu pekerjaan menyentuh piksel dalam jumlah nyata: praproses dataset gambar, pipeline penglihatan industri, deteksi sederhana di perangkat edge, atau fondasi riset. Untuk aplikasi yang hanya menampilkan gambar, library yang jauh lebih ringan sudah cukup; untuk inferensi model deep learning skala produksi, jalur yang lebih spesifik menunggu di Chapter berikutnya — ONNX Runtime.

Referensi: OpenCV 4.x Documentation , opencv.org .