LoginSignup
1
1

More than 3 years have passed since last update.

ROS講座122 mjpeg_streamを受信する

Posted at

環境

この記事は以下の環境で動いています。

項目
CPU Core i5-8250U
Ubuntu 18.04
ROS Melodic
Gazebo 9.0.0
python 2.7.17
OpenCV 3.2.0
Qt 5.5.1

インストールについてはROS講座02 インストールを参照してください。
またこの記事のプログラムはgithubにアップロードされています。ROS講座11 gitリポジトリを参照してください。

概要

以前(62 Webカメラの画像をブラウザから見る)にsensor_msgs/Imageの画像をwebブラウザで見るということをしました。今回はこれをc++のプログラムから見ていきましょう。

動画の形式

動画の配信形式はいろいろありますが、web-video-serverではmotion-jpeg(mjpeg)という形式で配信します。またこの形式のストリームはOpenCVを使って受信することが出来ます。

OpenCVで受信して表示する

ソースコード(cpp)

ros_lecture/app_lecture/src/mjpeg_opencv.cpp
#include <unistd.h>
#include <memory>
#include <iostream>
#include <thread>
#include <functional>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>

class MjpegView
{
public:
  MjpegView(std::string target, std::string topic)
  {
    std::string uri = "http://" + target + "/stream?topic=" + topic;
    if (!vcap_.open(uri))
    {
      std::cout << "Error opening video stream or file" << std::endl;
      return;
    }
    cv::namedWindow("Output Window");
    recv_thread_ = std::thread(std::bind(&MjpegView::loop, this));
  }

private:
  void loop(void)
  {
    while (true)
    {
      cv::Mat image;
      if (!vcap_.read(image))
      {
        std::cout << "No frame" << std::endl;
        return;
      }
      cv::imshow("Output Window", image);
      cv::waitKey(1);
    }
  }

  cv::VideoCapture vcap_;
  std::thread recv_thread_;
};

int main(int, char**)
{
  MjpegView mjpeg_view("localhost:8080", "/camera/image_raw");
  while (true)
  {
    sleep(1);
  }
}
  • cv::VideoCaptureがmjpeg sreamを受信するクラスです。open()で引数はmjpeg streamへのuriです。webブラウザでみえるurlを入れとけばアクセスします。
  • read()関数で受信したmjpegのフレームを取得します。この関数は取得するまでブロックします。

ソースコード(CMake)

ros_lecture/app_lecture/CMakeLists.txt
find_library(PTHREAD_LIBRARY pthread) # この行を追加
find_package(OpenCV REQUIRED) # この行を追加

include_directories(
# include
  ${catkin_INCLUDE_DIRS}
  ${OpenCV_INCLUDE_DIRS} # この行を追加
  Simple-WebSocket-Server
)

add_executable(mjpeg_opencv src/mjpeg_opencv.cpp) # この行を追加

target_link_libraries(mjpeg_opencv
  ${PTHREAD_LIBRARY} # この行を追加
  ${OpenCV_LIBRARIES} # この行を追加
)
  • OpenCVへの依存を追加しました。
  • もしcatkinのリンクをしない場合はpthreadへのリンクが必要です。

ビルド

cd ~/catkin_ws
catkin_make

実行

1つ目のターミナル
roslaunch cam_lecture sim_only.launch
2つ目のターミナル
rosrun web_video_server web_video_server 
3つ目のターミナル
rosrun app_lecture mjpeg_opencv

以下のようなOpenCVの画面が出ます。
app_mjpeg1.png

OpenCVで受信してQtで表示する。

QtのほうがOpenCVよりもほかの要素と組み合わせてGUIを作るのに向いています。OpenCVで受信して、それをQtの形式に変換してQtで表示してみます。

ソースコード(cpp)

ros_lecture/app_lecture/src/mjpeg_qt.cpp
#include <future>
#include <unistd.h>
#include <memory>
#include <iostream>
#include <thread>
#include <functional>
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <QApplication>
#include <QLabel>
#include <QImage>

class MjpegView
{
public:
  MjpegView(std::string target, std::string topic)
  {
    std::string uri = "http://" + target + "/stream?topic=" + topic;
    if (!vcap_.open(uri))
    {
      std::cout << "Error opening video stream or file" << std::endl;
      return;
    }
    label_ = new QLabel("######## basic1 ########");
    label_->show();
    recv_thread_ = std::thread(std::bind(&MjpegView::loop, this));
  }

private:
  void loop(void)
  {
    while (true)
    {
      cv::Mat image;
      if (!vcap_.read(image))
      {
        std::cout << "No frame" << std::endl;
        return;
      }
      cv::Mat rgb_image;
      cv::cvtColor(image, rgb_image, cv::COLOR_BGR2RGB);
      QImage qimage(rgb_image.data, rgb_image.cols, rgb_image.rows, QImage::QImage::Format_RGB888);
      label_->resize(rgb_image.cols, rgb_image.rows);
      label_->setPixmap(QPixmap::fromImage(qimage));
    }
  }

  cv::VideoCapture vcap_;
  QLabel* label_;
  std::thread recv_thread_;
  std::thread qt_thread_;
};

int main(int argc, char** argv)
{
  QApplication app(argc, argv);
  MjpegView mjpeg_view("localhost:8080", "/camera/image_raw");
  return app.exec();
}
  • Qtでは画像を表示するWidgetというものはありません。何らかのWidgetにpixmapを割り当てるという方法で画像を表示します。今回はQLabelを使います。
  • OpenCVのcv::Mat形式からQtのQImage形式に変換する必要があります。まず色の順番をBGR->RGBに並べ替えます。その後でQImageの形式に変えます。
  • setPixmap(QPixmap::fromImage(qimage))で特定のWidgetに画像をセットします。

ソースコード(CMake)

set(CMAKE_AUTOMOC ON)
find_package(Qt5 ${rviz_QT_VERSION} EXACT REQUIRED
  Core
  Widgets
)
set(QT_LIBRARIES Qt5::Widgets)
add_definitions(-DQT_NO_KEYWORDS)

add_executable(mjpeg_qt src/mjpeg_qt.cpp)

target_link_libraries(mjpeg_qt
  ${PTHREAD_LIBRARY}
  ${OpenCV_LIBRARIES} 
  ${QT_LIBRARIES}
)

Qtをビルドする設定を加えています。

ビルド

cd ~/catkin_ws
catkin_make

実行

1つ目のターミナル
roslaunch cam_lecture sim_only.launch
2つ目のターミナル
rosrun web_video_server web_video_server 
3つ目のターミナル
rosrun app_lecture mjpeg_qt

以下のようなQtの画面が出ます。
app_mjpeg2.png

参考

OpenCVでmjpgを受信
Opencv->Qtの変換

目次ページへのリンク

ROS講座の目次へのリンク

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1