研究開発日誌

CG研究・開発のちょっとしたメモ書き

ImageView

2014-12-24 OpenCV UI

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

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

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

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

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)のようにしてレンダリングできるようにしたいと思います.

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


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の実装.

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を呼んでデータを修正しています.