モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

工作HardwareHub ロゴ画像 (Laptop端末利用時)
工作HardwareHub ロゴ画像 (Mobile端末利用時)
目次目次を開く/閉じる

OpenCV を C++ から扱うためのサンプルコード

モーダルを閉じる

ステッカーを選択してください

モーダルを閉じる

お支払い内容をご確認ください

購入商品
「」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

公開日公開日
2019/10/31
最終更新最終更新
2024/05/27
記事区分記事区分
一般公開

Python から扱う方法ではなく C++ で OpenCV を扱うためのサンプルコードを記載します。ビルドには cmake を用います。

Debian の場合は以下のコマンドで必要なライブラリがインストールされます。

sudo apt install libopencv-dev

画像を開いてウィンドウに表示

main.cpp

#include <opencv2/opencv.hpp>

int main() {
    cv::Mat img = cv::imread("aaa.png", -1);
    if(img.empty()) {
        return -1;
    }
    cv::namedWindow("Example", cv::WINDOW_AUTOSIZE);
    cv::imshow("Example", img);
    cv::waitKey(0);
    cv::destroyWindow("Example");
    return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.1)
project( DisplayImage )
find_package( OpenCV REQUIRED )
add_executable( DisplayImage main.cpp )
target_link_libraries( DisplayImage ${OpenCV_LIBS} )

ビルド例

mkdir -p build
cd build/
cmake ..
make

実行例

./DisplayImage

画像のピクセルの値を取得、設定

OpenCV は RGB ではなく BGR で画像を処理します。

#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    cv::Mat img = cv::imread("aaa.png", -1);
    cv::Vec3b bgr = img.at<cv::Vec3b>(10, 0);
    unsigned int b = bgr[0];
    unsigned int g = bgr[1];
    unsigned int r = bgr[2];
    std::cout << "(r,g,b) = (" << r << ", " << g << ", " << b << ")" << std::endl;

    bgr[0] = 0;
    img.at<cv::Vec3b>(10, 0) = bgr;

    return 0;
}

出力例

(r,g,b) = (254, 166, 92)

Python での出力と一致することを確認

from matplotlib import pyplot as plt
import matplotlib.image as mpimg
img = mpimg.imread('aaa.png')
for i in range(3): 
    print(img.item(10,0,i) * 255)

254.00000005960464
166.00000530481339
92.00000211596489

動画を開いてフレームを連続してウィンドウに表示

#include <opencv2/opencv.hpp>

int main() {
    cv::namedWindow("Example", cv::WINDOW_AUTOSIZE);
    cv::VideoCapture cap;
    cap.open("sample.mov");
    cv::Mat frame;
    while(true) {
        cap >> frame;
        if(frame.empty()) {
            break;
        }
        cv::imshow("Example", frame);
        if((char)cv::waitKey(33) >= 0) { // wait 33 msec for key
            break;
        }
    }
    return 0;
}

動画のフレーム数、サイズ、フレームポジションを確認および設定

#include <opencv2/opencv.hpp>

int main() {
    cv::VideoCapture cap;
    cap.open("sample.mov");
    int frames = (int)cap.get(cv::CAP_PROP_FRAME_COUNT);
    int w = (int)cap.get(cv::CAP_PROP_FRAME_WIDTH);
    int h = (int)cap.get(cv::CAP_PROP_FRAME_HEIGHT);
    std::cout << "frames: " << frames << std::endl;
    std::cout << "dimensions: (" << w << ", " << h << ")" << std::endl;
    cap.set(cv::CAP_PROP_POS_FRAMES, 123);
    int current_pos = (int)cap.get(cv::CAP_PROP_POS_FRAMES);
    std::cout << "current_pos: " << current_pos << std::endl;
    return 0;
}

出力例

frames: 659
dimensions: (1080, 720)
current_pos: 123

畳み込み処理

入力画像を 5x5 の領域で走査して、ガウス関数にしたがった重みをつけて各領域内の 25 画素の値を平均した値を、出力画像における 5x5 の領域の中心の画素値とするような、平滑化の変換は以下のようになります。

#include <opencv2/opencv.hpp>

int main() {
    cv::Mat in = cv::imread("aaa.png");
    cv::namedWindow("Example-in", cv::WINDOW_AUTOSIZE);
    cv::namedWindow("Example-out", cv::WINDOW_AUTOSIZE);
    cv::imshow("Example-in", in);
    cv::Mat out;
    cv::GaussianBlur(in, out, cv::Size(5,5), 3, 3);
    cv::imshow("Example-out", out);
    cv::Mat out2;
    cv::GaussianBlur(out, out2, cv::Size(5,5), 3, 3);
    cv::imshow("Example-out2", out2);
    cv::waitKey(0);
    return 0;
}

フィルタとなる 5x5 の行列はカーネルともよばれます。OpenCV にはカーネルを用いて畳み込みを行うカーネル関数が多数実装されており GaussianBlur はその一つです。変換の性質上、GaussianBlur を用いる際のカーネルのサイズは奇数である必要があります。第 4,5 引数はガウス関数の x 方向と y 方向の標準偏差です。例えば標準偏差を非常に小さくしたり、カーネルのサイズを 1x1 にしたりすると、出力画像は入力画像とほぼ同じになります。

#include <opencv2/opencv.hpp>

int main() {
    cv::Mat in = cv::imread("aaa.png");
    cv::namedWindow("Example-in", cv::WINDOW_AUTOSIZE);
    cv::namedWindow("Example-out", cv::WINDOW_AUTOSIZE);
    cv::imshow("Example-in", in);
    cv::Mat out;
    cv::GaussianBlur(in, out, cv::Size(5,5), 0.00001, 0.00001);
    cv::imshow("Example-out", out);
    cv::Mat out2;
    cv::GaussianBlur(out, out2, cv::Size(1,1), 3, 3);
    cv::imshow("Example-out2", out2);
    cv::waitKey(0);
    return 0;
}

畳み込み処理におけるカーネル関数としてデルタ関数を用いると、数ピクセル毎にサンプリングを行うことができます。結果として、例えば入力画像の2分の1のサイズの画像を出力することができ、このような変換をダウンサンプリングとよびます。隣接するピクセル同士の画素値の変化について、入力画像よりもダウンサンプリングによる出力画像の方が大きくなってしまう可能性があります。出力画像(信号)に高周波数が入ってしまうことを防ぐためには、ダウンサンプリングする前に、入力画像の平滑化を行います。つまり、入力画像の隣接するピクセル同士の画素値の変化が十分小さくなるような、ローパスフィルタをかけておきます。

cv::pyrDown() 関数を用いると、Gaussian による平滑化とダウンサンプリングを行うことができます。入力画像にダウンサンプリングを繰り返し適用していくと、解像度の異なる画像の集合が得られます。これを、画像ピラミッド、あるいはスケール空間とよびます。pyrDown の pyr はピラミッドを意味します。

#include <opencv2/opencv.hpp>

int main() {
    cv::Mat in, out;
    cv::namedWindow("Example-in", cv::WINDOW_AUTOSIZE);
    cv::namedWindow("Example-out", cv::WINDOW_AUTOSIZE);
    in = cv::imread("aaa.png");
    cv::imshow("Example-in", in);
    cv::pyrDown(in, out);
    cv::imshow("Example-out", out);
    cv::waitKey(0);
    return 0;
}

畳み込みは、輪郭線などのエッジを検出するためにも利用されます。隣接するピクセル同士の画素値の変化率を取得するような、微分を行うカーネル関数を用いれば、エッジにおける画素値の変化率が大きいという仮定のもと、エッジを検出できます。こちらのページに記載の cv::Canny はエッジ検出アルゴリズムの一つです。エッジ検出はノイズの影響を受けやすいため、内部的に Gaussian フィルタを用いて平滑化してからエッジ検出します。第3引数の閾値よりも変化率が小さい画素はエッジではないとします。第4引数の閾値よりも変化率が大きい画素はエッジであるとします。更に、エッジは連続しているという仮定のもと、変化率の大きさが第3引数と第4引数の間の画素は、第4引数の閾値よりも変化率が大きい画素と連続していればエッジであるとします。cv::Canny への入力画像のチャネル数は一つでよいため cv::cvtColor でグレー画像に変換 (cvt; convert) します。

#include <opencv2/opencv.hpp>

int main() {
    cv::Mat rgb, gry, cny;
    cv::namedWindow("Example Gray", cv::WINDOW_AUTOSIZE);
    cv::namedWindow("Example Canny", cv::WINDOW_AUTOSIZE);
    rgb = cv::imread("aaa.png");
    cv::cvtColor(rgb, gry, cv::COLOR_BGR2GRAY);
    cv::imshow("Example Gray", gry);
    cv::Canny(gry, cny, 10, 100);
    cv::imshow("Example Canny", cny);
    cv::waitKey(0);
    return 0;
}

動画の出力

入力動画と同じサイズの動画を出力する例です。上記 cv::Canny エッジ検出アルゴリズムで各フレームを変換しています。キーコード 27 は Esc です。動画の拡張子 avi に対応するコーデックは複数存在します。以下では XVID を指定しています。avi に対応するものとして、他に例えば MJPG (モーション JPG) があります。動画のコーデックは以下のように確認できます。

$ file out.avi
out.avi: RIFF (little-endian) data, AVI, 1080 x 720, >30 fps, video: XviD

$ file out.avi
out.avi: RIFF (little-endian) data, AVI, 1080 x 720, 30.00 fps, video: Motion JPEG

main.cpp

#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    cv::namedWindow("Example-in", cv::WINDOW_AUTOSIZE);
    cv::namedWindow("Example-out", cv::WINDOW_AUTOSIZE);

    cv::VideoCapture capture("sample.mov");

    double fps = capture.get(cv::CAP_PROP_FPS);
    cv::Size size((int)capture.get(cv::CAP_PROP_FRAME_WIDTH),
                  (int)capture.get(cv::CAP_PROP_FRAME_HEIGHT));

    cv::VideoWriter writer;
    writer.open("out.avi", CV_FOURCC('X', 'V', 'I', 'D'), fps, size, false);

    cv::Mat bgr, gry, cny;
    while(true) {
        capture >> bgr;
        if(bgr.empty()) {
            break;
        }
        cv::imshow("Example-in", bgr);
        cv::cvtColor(bgr, gry, cv::COLOR_BGR2GRAY);
        cv::Canny(gry, cny, 10, 100);
        cv::imshow("Example-out", cny);
        writer << cny;
        char c = (char)cv::waitKey(33);
        if(c == 27) {
            break;
        }
    }
    writer.release();
    capture.release();
    return 0;
}

0
詳細設定を開く/閉じる
アカウント プロフィール画像 (本文下)

インフラ構築と自動化が得意。TerraformとAnsibleでインフラを自動構築するお仕事が多め

記事の執筆者にステッカーを贈る

有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

さらに詳しく →
ステッカーを贈る コンセプト画像

Feedbacks

Feedbacks コンセプト画像

    ログインするとコメントを投稿できます。

    関連記事