特にSymbian向けのモバイルアプリケーションの開発分野での仕事中に、携帯電話のカメラを操作するためのいくつかのソリューションが作成されました。 時間の経過とともに、これらの決定は進化してきました。これについては、次の2つの記事で説明します。
1つ目は、QtMobility 1.1.3を使用したSymbian電話機からの自明ではないが柔軟な画像取得に焦点を当て、2つ目は、Nokia N900、N950、N9を現在実行しているMeego 1.2 Harmattanプラットフォームにコードを転送する際の問題とその解決策に焦点を当てます。
この資料は、モバイルアプリケーションのQt開発者を始めるのに役立ちます。
だから、Symbianカメラで作業するためのQt。
私が解決しようとしていた問題は同じままでした。カメラから画像を取得し、独自のファインダーに出力し、表示された画像の上にウィジェットを描画し、いくつかのビデオフレームをリモートサーバーに転送する必要がありました。 タスクの重要な側面は、ユーザーの介入なしにすべてのアクションを実行する必要があることです。必要なのは、アプリケーションを起動してカメラを向けるだけです。
QtMobilityのいくつかの機能により、決定が初めて生まれたのは初めてです。 ビューファインダー(QVideoWidgetまたはその子孫QCameraViewfinder)に画像を表示するために最も単純な直接的な方法を使用すると、次の問題が発生します。
- QVideoWidgetは、自身の上に半透明のウィジェットをレンダリングすることを許可しません(透明性のために黒い長方形のみが取得されます)
- 画像を写真形式のオブジェクトとしてのみ取得でき、ファイルシステムに保存する必要があります(QCameraImageCaptureを使用)
- 上記から、ビューファインダーの「内側」の画像を制御することは不可能である、つまり、画面上ではカメラ自体が見ているものだけが見えるということになる
さらに、QWidget :: render()を介してビューファインダーに表示された後に画像を取得しようとすると、QVideoWidget自体は各フレームで再描画されないため、黒い四角形のみが表示されます。 同じ理由で、ビューファインダーのQWidget :: paintEvent()メソッドを再定義することは無意味です。
タスクが最初に解決されたとき、ついにネイティブのSymbianコードを使用するようになりました。 結果はQObjectであり、新しいフレームごとにQPixmapで信号を送信します。 このピックスマップもアプリケーションで処理されました-描画、送信など。
そして、このオプションは完全に機能していましたが、他のプラットフォーム、またはむしろ別のプラットフォーム-Meegoに簡単かつ迅速に移植することができました。
次に、QtとQtMobilityのみを使用して問題の解決策を提案します。QtとQtMobilityは、(特にSymbian.C ++に精通していない人にとっては)より単純です。
以下のコードは、 公式の例に部分的に基づいています。この例では、無関係なものがたくさんあります。
したがって、Symbian ^ 3またはSymbian 5thエディションの携帯電話のカメラから、Qtツールを個別のオブジェクトとして使用してビデオフレームを取得します。 これを行うには、新しいフレームごとに新たにレンダリングされる独自のビューファインダーを作成します。
操作のスキームは次のとおりです。
- QAbstractVideoSurface抽象クラスの子孫を作成し、2つの純粋な仮想メソッドQAbstractVideoSurface :: supportedPixelFormats()およびQAbstractVideoSurface :: present()を定義します。 これは、カメラからのデータが送信されるフレームハンドラになります。
- present()メソッドでは、ビデオフレームをQImageオブジェクトに変換し、コールバックに転送します
- カメラをアプリケーションに接続し、カメラの新しい「サーフェス」(フレームハンドラー)を設定します-最初の段落のクラスのオブジェクト
- コールバックメソッドで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に移植し、後者を実行するデバイスのカメラのハードウェア機能に関連する修正について説明します。
使用されるソース:
ご清聴ありがとうございました。