環境
この記事は以下の環境で動いています。
項目 | 値 |
---|---|
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で受信して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