ImageView

QtでOpenCVの画像を表示させるためのWidget.

OpenCVではcv::imshow("imageName", imageData )関数を使って簡単に画像を表示できるようになっています.ただ,UI周りの機能があまり充実していないので,UIを色々と使いたいプログラムを書くとなると別のUIライブラリを使用する必要が出てくると思います.

色々な選択肢が考えられると思いますが,このブログでは,主にQtを使ってUIを作成しています.Qtを使ってUIを作成する場合,cv::imshowで表示されるOpenCVウィンドウではなく,Qt独自のWidgetを使ってOpenCVの画像を表示すると色々なUIと組み合わせることができて便利です.

以下がQtでOpenCVの画像を表示するためのサンプルコードになります.

ImageView.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ImageView : public QWidget
{
Q_OBJECT
public:
//! Constructor.
ImageView(QWidget* parent = 0);
public slots:
//! Render OpenCV image on the view.
void renderCVImage( const cv::Mat& image );
protected:
//! paintEvent override for rendering OpenCV image.
void paintEvent(QPaintEvent *event);
private:
QImage _qImage;
cv::Mat _image;
};

ImageViewクラスはQWidgetを継承して作成し,paintEvent関数をオーバーライドしてOpenCVの画像を表示できるようカスタマイズします.実際にOpenCVのレンダリングの画像をレンダリングしたいときは,imageView->renderCVImage(image)のようにしてレンダリングできるようにしたいと思います.

以下,実装の関連部分になります.

ImageView.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void ImageView::renderCVImage( const cv::Mat &image )
{
if (image.empty()) return;
// Convert OpenCV image to QImage
_qImage = QOpenCVImage::CVMatToQImage( image );
_image = image;
update();
}
void ImageView::paintEvent(QPaintEvent *event)
{
QPainter painter( this );
if ( _qImage.width() == 0 || _qImage.height() == 0)
{
return;
}
// Paint QImage on the view.
painter.drawImage( QPoint( 0, 0 ), _qImage );
}

renderCVImageの実装では,cv::Mat型のimageをQOpenCVImage::CVMatToQImageでQImageに変換し,update関数を呼ぶことにより,その結果paintEventメソッドが呼んでいます.paintEventの中では,単に_qImageに保持しておいた画像をQPainterを使って描画しているだけです.

以下は,QOpenCVImage::CVMatToQImageの実装.

QOpenCVImage.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const QImage QOpenCVImage::CVMatToQImage(const cv::Mat& cvImage)
{
int dim = cvImage.channels();
cv::Mat cvImage_8U = CVImage::to8U(cvImage);
cv::Mat imageData = cv::Mat(cvImage_8U.size(), CV_8UC3, cv::Scalar::all(0) );
if (dim==4)
{
int from_to[] = { 0,0, 1,1, 2,2 };
cv::mixChannels( &cvImage_8U, 1, &imageData, 1, from_to, 3 );
imageData = CVImage::setAlphaRegion( imageData, CVImage::alpha(cvImage_8U));
}
if (dim==3)
{
imageData = cvImage_8U;
}
if (dim==2)
{
int from_to[] = { 0,0, 1,1 };
cv::mixChannels( &cvImage_8U, 1, &imageData, 1, from_to, 2 );
}
if(dim == 1) {
cv::cvtColor(cvImage_8U, imageData, CV_GRAY2BGR);
}
QImage qImage( (const unsigned char*) imageData.data, imageData.cols, imageData.rows, imageData.step , QImage::Format_RGB888);
return qImage.rgbSwapped();
}

基本的には,cv::MatのデータをQImageに変換するだけですが,入力のcv::Matデータの型に応じた調整を行う必要があります.この実装では,CV_32FC3, CV_8UC2のようなcv::Mat型を一度CV_8UC3型に揃えてから変換処理を行っています.cv::MatのデータとQImageではビット配列の順番が違うので,rgbSwappedを呼んでデータを修正しています.