MJPEGで画像を取得する (c++/Linux)

 

 

 

本ページでは、i-PRO カメラとPCを MJPEG により接続してPC画面へ表示するプログラムを c++ (Linux) で作成する例を紹介します。とても短いプログラムで i-PRO カメラの映像を見ることができます。動作確認は i-PRO mini (WV-S7130)、モジュールカメラ(AIスターターキット)を使って行いましたが、ほとんどの i-PRO カメラでそのまま利用できるはずです。ぜひお試しください。

 

[動画] MJPEG でカメラと接続して映像表示した様子

 


 

"i-PRO mini" 紹介:

i-PRO mini 画像

 

"モジュールカメラ" 紹介:

AI スターターキット 画像(1) AI スターターキット 画像(2)

 

カメラ初期設定についてはカメラ毎の取扱説明書をご確認ください。

カメラのIPアドレスを確認・設定できる下記ツールを事前に入手しておくと便利です。

 



 

1.MJPEG 表記仕様

MJPEG で接続するための表記を以下に記載します。

「ネットワークカメラCGIコマンドインターフェース仕様書 統合版」[1] で下記に記載されている情報を元に加筆などしています。

 

http://<user-id>:<user-password>@<カメラのIPアドレス>/nphMotionJpeg?Resolution=<解像度>&Quality=<品質>&Framerate=<フレームレート>

(例) http://admin:password@192.168.0.10/nphMotionJpeg?Resolution=1920x1080&Quality=Standard&Framerate=15

 

注意事項

 

 

2. i-PRO カメラと MJPEG 接続して映像を表示してみる

[概要]

Linux の c++ と OpenCV を使って、PC と i-PRO カメラを MJPEG(Motion JPEG) で接続して映像表示してみます。

今回の手法ではMPEGの取得をOpenCVに任せます。

 

[評価環境]

開発環境 :

GNU
build-essensial
CMake
9.4.0
12.8
3.16.3
ライブラリ : OpenCV 4.6.0
OS : ubuntu

20.04 LTS

 

[環境準備]

以下のコマンドを実施して環境を準備します。

sudo apt update -y && sudo apt upgrade -y
sudo apt install build-essential libopencv-dev cmake -y
sudo apt update -y && sudo apt upgrade -y

 

 

[プログラム]

サンプルプログラムのソースコードを以下に示します。

 

[プログラムソース "opencv_mjpeg.cpp"]

/*
======================================================================================

[Abstract]
    Try connecting to an i-PRO camera with MJPEG.
    MJPEG で i-PRO カメラと接続してみる

[Details]
    Let's try first.
    まずはやってみる

[Library install]
    sudo apt update -y && sudo apt upgrade -y
    sudo apt install build-essential libopencv-dev cmake -y
    sudo apt update -y && sudo apt upgrade -y

======================================================================================
*/

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


const std::string user_id   = "user_id";         // Change to match your camera setting
const std::string user_pw   = "user_pw";         // Change to match your camera setting
const std::string host      = "192.168.0.10";    // Change to match your camera setting
const std::string winname   = "VIDEO";           // Window title
const std::string framerate = "15"

const std::string url = "http://" + user_id + ":" + user_pw + "@" + host + "/cgi-bin/nphMotionJpeg?Framerate=" + framerate ;


int main(int argc, const char* argv[])
{
    cv::VideoCapture cap(url);
    cv::Mat frame;
    char ret;

    while (true) {
        cap >> frame;
        if (frame.empty()) {
            break;
        }

        // Please modify the value to fit your PC screen size.
        resize(frame, frame, cv::Size(), 0.5, 0.5);      // Setting by magnification.

        // Display video.
        cv::imshow(winname, frame);

        ret = (char)cv::waitKey(1);     // necessary to display the video by cv::imshow().

        // Press the "q" key to finish.
        if (ret == 'q') {
            break;
        }
    }

    cap.release();
    cv::destroyAllWindows();

    return EXIT_SUCCESS;
}

このソースコードはRTSPのソースコードとほぼ同じとなります。

 

 

[ビルド]

ビルドにおいて環境依存を極力するなくするため、以下の手順でビルドします。

  1. CMakeでMakefileを生成する
  2. 生成したMakefileを使ってビルドする

 

ソースコード(opencv_mjpeg.cpp)と同じディレクトリに以下のように中身が書かれたCMakeLists.txtを配置してください。

CMake の知識がある方は、内容をカスタマイズしてお使いください。

 

["CMakeLists.txt"]

# 変数SOURCE_CORDEを宣言し、ソースコードのファイル名の拡張子無くした値を入れる。
# cmake -D SOURCE_CODE=(ソース名)で上書き可
set(SOURCE_CODE opencv_mjpeg CACHE NAME "Target object name")

# CMakeの最低バージョンを記述
cmake_minimum_required(VERSION 2.8)

# ソリューション名を指定
project( ${SOURCE_CODE} )

# OpenCVのパッケージを探す
find_package( OpenCV REQUIRED )

#ヘッダファイルのパスを指定
include_directories( ${OpenCV_INCLUDE_DIRS} )

# 実行ファイル名とソース指定(ここではソースと同じ名前の実行ファイルを作ります)
add_executable( ${SOURCE_CODE} ${SOURCE_CODE}.cpp )

#リンク先のライブラリを指定
target_link_libraries( ${SOURCE_CODE} ${OpenCV_LIBS})

 

ビルド環境を作成してアプリケーションを構築するため、以下のコマンドを実施します。

mkdir build && cd build
cmake ..
make

 

これにより、buildディレクトリに[opencv_jpeg]という実行ファイルができましたので、以下のようにコマンドを実施すればアプリケーションが実施されます

./opencv_mjpeg

 

ソースコードの変更をする場合は、すでにビルド環境ができていますので、buildディレクトリで以下のようにすれば更新が可能です。

ライブラリを追加する場合は CMakeLists.txtの追記を行い、build ディレクトリを削除してcmakeをやり直してください。

make clean
make

 

 

3. Curlをつかってi-PRO カメラと MJPEG 接続して映像を表示してみる

[概要]

Linux の c++ と OpenCV を使って、PC と i-PRO カメラを MJPEG(Motion JPEG) で接続して映像表示してみます。

MJPEGのヘッダーの情報が欲しい時はこちらのソースコードを参考にしてみてください。

 

[評価環境]

開発環境 :

GNU
build-essensial
CMake
9.4.0
12.8
3.16.3
ライブラリ : OpenCV
libcurl4
4.6.0
7.68.0-1ubuntu2.14 amd64
OS : ubuntu 20.04 LTS

※注意:このソースでは"gcc extensions" を利用しています。標準仕様ではないので gcc/g++ 以外ではエラーになります。

 

 

[環境準備]

以下のコマンドを実施して環境を準備します。

sudo apt update -y && sudo apt upgrade -y
sudo apt install build-essential libopencv-dev cmake -y
sudo apt install libcurl4-openssl-dev -y
sudo apt update -y && sudo apt upgrade -y

 

 

[プログラム]

サンプルプログラムのソースコードを以下に示します。

 

[プログラムソース "opencv_mjpeg_curl.cpp"]

/*
======================================================================================

[Abstract]
Try connecting to an i-PRO camera with MJPEG by curl.
RTSP で i-PRO カメラと接続してみる

[Details]
Let's try first.
まずはやってみる

[Library install]
sudo apt update -y && sudo apt upgrade -y
sudo apt install build-essential libopencv-dev cmake -y
sudo apt install libcurl4-openssl-dev -y 
sudo apt update -y && sudo apt upgrade -y

======================================================================================
*/

#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
#include <opencv2/core/utility.hpp>
#include <curl/curl.h>


const std::string user_id = "user-id"; // Change to match your camera setting
const std::string user_pw = "password"; // Change to match your camera setting
const std::string host = "192.168.0.10"; // Change to match your camera setting
const std::string winname = "VIDEO"; // Window title
const std::string framerate = "15"; // Frame rate (indispensable param)

const std::string cameraURL = "http://" + host + "/cgi-bin/nphMotionJpeg?Framerate=" + framerate;

// data receive callback function
size_t onReceive(void *ptr, size_t size, size_t nmemb, void *userdata)
{
    //important static flag
    static bool isSOI = false;

    //receivebuffer handover
    std::vector<char>* receiveBuffer = (std::vector<char>*)userdata;

    const size_t segsize = size * nmemb; 

    //Find SOI/EOI and get position and raise a flag
    int PosS=0;      //start positon
    int PosE=0;      // end  positon
    int status=0;    //0:Non,1:EOI,2:SOI,3:EOI->SOI
    bool isJPEG=false;

    //Finding SOI and EOI
    for(int i=0;i<segsize-1;i++){
        //find EOI
        if(*((char*)ptr+i)==(char)0xFF && *((char*)ptr+i+1)==(char)0xD9 && isSOI==true){
            isSOI=false;
            PosE=i+1;//eoi position
            status=1;
        }

        //find SOI
        if(*((char*)ptr+i)==(char)0xFF && *((char*)ptr+i+1)==(char)0xD8 && isSOI==false){
            isSOI=true;
            PosS=i; //soi positon
            if(status==1){ //EOI -> neer -> SOI
                status=3;
            }else{
                status=2;
            } 
            break; //start JPEG Image
        }
    } 


    //Fill jpeg data into receiveBuffer
    if(isSOI==true && status==0){
        receiveBuffer->insert(receiveBuffer->end(), (char*)ptr, (char*)ptr+segsize);
    }else if(status==2){
        receiveBuffer->insert(receiveBuffer->end(), (char*)ptr+PosS, (char*)ptr+segsize);
    }else if(status==1 || status==3){
        receiveBuffer->insert(receiveBuffer->end(), (char*)ptr, (char*)ptr+PosE);
        isJPEG=true; //complete JPEG 
    }


    //display JPEG
    if(isJPEG==true){
        try{
            // JPEG decore
            cv::Mat frame = cv::imdecode(*receiveBuffer, cv::IMREAD_UNCHANGED);

            //clear recv_buffer
            receiveBuffer->clear();

            // Please modify the value to fit your PC screen size.
            resize(frame, frame, cv::Size(), 0.5, 0.5);

            cv::imshow(winname, frame); //Display video.

            if((char)cv::waitKey(1) >= 0) { // wait for any key
                return 0; 
            } 
        }catch(...){
            //clear recv_buffer
            receiveBuffer->clear(); 
        }
    }

    //Fill jpeg data into receiveBuffer
    if(status==3){ //EOI -> neer -> SOI : recv_buf need to add data
        receiveBuffer->insert(receiveBuffer->end(), (char*)ptr+PosS, (char*)ptr+segsize); 
    }

    return segsize;
}


int main(int argc, char* argv[])
{

    std::vector<char> receiveBuffer;
    CURL* curl = NULL;
    CURLcode res; 

    //init curl processing
    curl = curl_easy_init();
    if (!curl) {
        printf("curl_easy_init() failed...\n");
        return 0;
    }

    //for digest
    curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
    std::string user_id_pw =user_id + ":" + user_pw;
    curl_easy_setopt(curl, CURLOPT_USERPWD, user_id_pw.c_str());

    //curl option
    curl_easy_setopt(curl, CURLOPT_URL, cameraURL.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &receiveBuffer); 
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, onReceive);

    //In the mjpeg case, curl_easy_perform continues
    res = curl_easy_perform(curl);

    //curl processing finished
    curl_easy_cleanup(curl);
    receiveBuffer.clear();

    cv::destroyAllWindows();

    return 0;
}

MJPEGはストリームになるため、一度curl_easy_performが実行されると、ネットワーク切断が発生するまでcurl_easy_performが実行され続けます。
そこで、curl_easy_setoptでコールバック関数を呼び出すようにして、コールバック関数で受信バッファにJPEGデータを貯めこむようにしています。
MJPEGのストリームの区切りは、EOI SOIでわかるため、コールバック関数単位に受け取ったデータの中身を確認してSOIを見つけ出し、
EOIを受信するまで、受信バッファを貯めこむ動きをします。JPEGのデコードが終わったら、受信バッファをクリアしています。

 

 

[ビルド]

ビルドにおいて環境依存を極力するなくするため、以下の手順でビルドします。

  1. CMakeでMakefileを生成する
  2. 生成したMakefileを使ってビルドする

 

ソースコード(opencv_mjpeg_curl.cpp)と同じディレクトリに以下のように中身が書かれたCMakeLists.txtを配置してください。

CMake の知識がある方は、内容をカスタマイズしてお使いください。

 

["CMakeLists.txt"]

# 変数SOURCE_CORDEを宣言し、ソースコードのファイル名の拡張子無くした値を入れる。
# cmake -D SOURCE_CODE=(ソース名)で上書き可
set(SOURCE_CODE opencv_mjpeg_crel CACHE NAME "Target object name")

# CMakeの最低バージョンを記述
cmake_minimum_required(VERSION 2.8)

# ソリューション名を指定
project( ${SOURCE_CODE} )

# OpenCVのパッケージを探す
find_package( OpenCV REQUIRED )

#ヘッダファイルのパスを指定
include_directories( ${OpenCV_INCLUDE_DIRS} )

# CURLのパッケージを探す
find_package( CURL REQUIRED )

#ヘッダファイルのパスを指定
include_directories( ${CURL_INCLUDE_DIR} )

# 実行ファイル名とソース指定(ここではソースと同じ名前の実行ファイルを作ります)
add_executable( ${SOURCE_CODE} ${SOURCE_CODE}.cpp )

#リンク先のライブラリを指定
target_link_libraries( ${SOURCE_CODE} ${OpenCV_LIBS} ${CURL_LIBRARIES})

 

ビルド環境を作成してアプリケーションを構築するため、以下のコマンドを実施します。

mkdir build && cd build
cmake ..
make

 

これにより、buildディレクトリに[opencv_jpeg]という実行ファイルができましたので、以下のようにコマンドを実施すればアプリケーションが実施されます

./opencv_mjpeg_curl

 

ソースコードの変更をする場合は、すでにビルド環境ができていますので、buildディレクトリで以下のようにすれば更新が可能です。

ライブラリを追加する場合は CMakeLists.txtの追記を行い、build ディレクトリを削除してcmakeをやり直してください。

make clean
make

 

 

ライセンス

本ページの情報は、特記無い限り下記 MIT ライセンスで提供されます。

The MIT License (MIT)

  Copyright 2023 Kinoshita Hidetoshi

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 

 

参考

 


 

変更履歴

2025/11/17 - 「[1] ネットワークカメラCGIコマンドインターフェース仕様書」 リンク先を更新, 木下英俊
2023/3/22 - 新規作成, 木下英俊

 

Programming Items トップページ

プライバシーポリシー