Qtとモバイルカメラ。 パート1、Symbian

こんにちは、Habr!



特にSymbian向けのモバイルアプリケーションの開発分野での仕事中に、携帯電話のカメラを操作するためのいくつかのソリューションが作成されました。 時間の経過とともに、これらの決定は進化してきました。これについては、次の2つの記事で説明します。

1つ目は、QtMobility 1.1.3を使用したSymbian電話機からの自明ではないが柔軟な画像取得に焦点を当て、2つ目は、Nokia N900、N950、N9を現在実行しているMeego 1.2 Harmattanプラットフォームにコードを転送する際の問題とその解決策に焦点を当てます。

この資料は、モバイルアプリケーションのQt開発者を始めるのに役立ちます。



だから、Symbianカメラで作業するためのQt。



私が解決しようとしていた問題は同じままでした。カメラから画像を取得し、独自のファインダーに出力し、表示された画像の上にウィジェットを描画し、いくつかのビデオフレームをリモートサーバーに転送する必要がありました。 タスクの重要な側面は、ユーザーの介入なしにすべてのアクションを実行する必要があることです。必要なのは、アプリケーションを起動してカメラを向けるだけです。



QtMobilityのいくつかの機能により、決定が初めて生まれたのは初めてです。 ビューファインダー(QVideoWidgetまたはその子孫QCameraViewfinder)に画像を表示するために最も単純な直接的な方法を使用すると、次の問題が発生します。



さらに、QWidget :: render()を介してビューファインダーに表示された後に画像を取得しようとすると、QVideoWidget自体は各フレームで再描画されないため、黒い四角形のみが表示されます。 同じ理由で、ビューファインダーのQWidget :: paintEvent()メソッドを再定義することは無意味です。



タスクが最初に解決されたとき、ついにネイティブのSymbianコードを使用するようになりました。 結果はQObjectであり、新しいフレームごとにQPixmapで信号を送信します。 このピックスマップもアプリケーションで処理されました-描画、送信など。

そして、このオプションは完全に機能していましたが、他のプラットフォーム、またはむしろ別のプラットフォーム-Meegoに簡単かつ迅速に移植することができました。



次に、QtとQtMobilityのみを使用して問題の解決策を提案します。QtとQtMobilityは、(特にSymbian.C ++に精通していない人にとっては)より単純です。

以下のコードは、 公式の例に部分的に基づいています。このでは、無関係なものがたくさんあります。



したがって、Symbian ^ 3またはSymbian 5thエディションの携帯電話のカメラから、Qtツールを個別のオブジェクトとして使用してビデオフレームを取得します。 これを行うには、新しいフレームごとに新たにレンダリングされる独自のビューファインダーを作成します。

操作のスキームは次のとおりです。

  1. QAbstractVideoSurface抽象クラスの子孫を作成し、2つの純粋な仮想メソッドQAbstractVideoSurface :: supportedPixelFormats()およびQAbstractVideoSurface :: present()を定義します。 これは、カメラからのデータが送信されるフレームハンドラになります。
  2. present()メソッドでは、ビデオフレームをQImageオブジェクトに変換し、コールバックに転送します
  3. カメラをアプリケーションに接続し、カメラの新しい「サーフェス」(フレームハンドラー)を設定します-最初の段落のクラスのオブジェクト
  4. コールバックメソッドでQImageオブジェクトを受け取ったら、自由裁量で処理します。この場合、ビューファインダーのpaintEvent()でそれを描画します。


最初に、イメージ(フレーム)が送信されるメソッドにコールバックを作成します。 原則として、信号スロットペアで置き換えることができますが、この特定の方法を使用します。 このクラスは、最終ビューファインダーによって継承される必要があります。

PS私はライブラリを接続することを示しません-すべての必要なQtクラスは通常通りに接続できます-例えば、名前で
#include <QAbstractVideoSurface>
      
      





> videosurfaceimageobserver.h

 class VideoSurfaceImageObserver { public: virtual void newImage(QImage) = 0; };
      
      





次は、QAbstractVideoSurfaceから継承したクラスの作成です。

> myvideosurface.h

 class myVideoSurface : public QAbstractVideoSurface { Q_OBJECT public: myVideoSurface(VideoSurfaceImageObserver *mObserver, QObject *parent = 0); bool present(const QVideoFrame &frame); QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType type=QAbstractVideoBuffer::NoHandle ) const; private: QVideoFrame m_frame; QImage::Format m_imageFormat; QVideoSurfaceFormat m_videoFormat; VideoSurfaceImageObserver *observer; };
      
      





> myvideosurface.cpp

 myVideoSurface::myVideoSurface(VideoSurfaceImageObserver *mObserver, QObject *parent) : QAbstractVideoSurface(parent) { //   ,   newImage()  //    observer = mObserver; m_imageFormat = QImage::Format_Invalid; } /** *  ,     *  video surface' * Symbian      */ QList<QVideoFrame::PixelFormat> myVideoSurface::supportedPixelFormats( QAbstractVideoBuffer::HandleType handleType) const { if (handleType == QAbstractVideoBuffer::NoHandle) { return QList<QVideoFrame::PixelFormat>() << QVideoFrame::Format_RGB32 << QVideoFrame::Format_ARGB32 << QVideoFrame::Format_ARGB32_Premultiplied << QVideoFrame::Format_RGB565 << QVideoFrame::Format_RGB555; } else { return QList<QVideoFrame::PixelFormat>(); } } /** *   . *,       QVideoFrame, *     QImage     */ bool myVideoSurface::present(const QVideoFrame &frame){ m_frame = frame; // " " -       //,    surface'. //       , //       if(surfaceFormat().pixelFormat() != m_frame.pixelFormat() || surfaceFormat().frameSize() != m_frame.size()) { stop(); return false; } else { //   QImage... if (m_frame.map(QAbstractVideoBuffer::ReadOnly)) { QImage image( m_frame.bits(), m_frame.width(), m_frame.height(), m_frame.bytesPerLine(), m_imageFormat); //...      observer->newImage(image); } return true; } }
      
      





最後に、ビューファインダークラスを作成します。

> myviewfinder.h

 class myViewFinder: public QWidget, public VideoSurfaceImageObserver { Q_OBJECT public: explicit myViewFinder(QWidget *parent = 0); virtual ~myViewFinder(); //     void newImage(QImage); private: void paintEvent(QPaintEvent *); private: QCamera* camera_; myVideoSurface *surface; QImage *pix; };
      
      





>myviewfinder.pp

 myViewFinder::myViewFinder(QWidget *parent) : QWidget(parent), camera_(0), viewfinder_(0), pix(0) { //,   -  camera_ = new QCamera; //  video surface     //   surface = new myVideoSurface(this, this); //     pix = new QImage(); //---      //---  video surface QVideoRendererControl *control = qobject_cast<QVideoRendererControl *>( camera_->service()->requestControl("com.nokia.Qt.QVideoRendererControl/1.0")); if(control){ control->setSurface(surface); } //--- // ,     // myVideoSurface    camera_->start(); } /** *      , *  QImage */ void myViewFinder::newImage(QImage img){ // ... &pix = img; //...   . //  update() //   paintEvent() //  update(); } void myViewFinder::paintEvent(QPaintEvent *event){ QPainter painter(this); //  . // -  . painter.fillRect(this->geometry(), Qt::black); if(!pix) return; //     //  .    //    video surface //    if(!pix->isNull() && this->geometry().width() != 0 && this->geometry().height() != 0 && pix->width() != 0 && pix->height() != 0) { //   ... QRect pixR(pix->rect()); pixR.moveCenter(this->geometry().center()); //... ! painter.save(); painter.drawImage(pixR, *pix); painter.restore(); } } myViewFinder::~myViewFinder() { camera_->unload(); delete viewfinder_; delete camera_; delete pix; }
      
      





次に、画像をレンダリングする前にビューファインダーを黒で埋める理由について説明します。 Symbian ^ 3およびSymbian 5thエディションの携帯電話の画面サイズは640x360で、カメラの解像度は少なくとも640x480です。 そのため、たとえば、ビューファインダー以外にフルスクリーンアプリケーションに何もない場合、アスペクト比により、画面全体をカメラ画像で埋めることはできません。 これを行うには、最初にキャンバスを黒(または他の色や画像)で塗りつぶし、デスクトップや他のウィジェットがファインダーの周りに映らないようにします。



端に黒いバーがないビューファインダーでウィジェット全体を塗りつぶすが、画像の端をトリミングするには、painEvent()を次のように変更できます。

 void myViewFinder::paintEvent(QPaintEvent *event){ QPainter painter(this); if(!pix) return; if(!pix->isNull() && this->geometry().width() != 0 && this->geometry().height() != 0 && pix->width() != 0 && pix->height() != 0) { //  ,      QPixmap newPix = pix->scaled(this->size(), Qt::KeepAspectRatioByExpanding); QRect pixR(newPix.rect()); pixR.moveCenter(this->geometry().center()); //... ! painter.save(); painter.drawImage(pixR, newPix ); painter.restore(); } }
      
      





したがって、本格的なビューファインダーが得られ、さらに通常のウィジェットとして使用できます。 さらに、フレームがnewImage()に到着した後、それで何でもできます。これはQVideoWidgetよりも有利です。



次の記事では、このコードをMeego 1.2 Harmattanに移植し、後者を実行するデバイスのカメラのハードウェア機能に関連する修正について説明します。



使用されるソース:

  1. 同じ例
  2. QtおよびQtMobilityの他の公式ドック


ご清聴ありがとうございました。



All Articles