Posted by & filed under memo, programming.


create texture with OpenCV自作 Bitmap 読み込みクラスがうまいこと動いてくれないので、Mac に OpenCV をインストールしてそれを使って画像を読み込み、テクスチャを生成してみた。

.

.

.

.

.

.

OpenCV の導入

Mac には OpenCV は標準では入っていない。そこで、自分でビルド・インストールする必要がある。

一番手っ取り早い方法は、 macports を使う方法だ。

$ sudo port install opencv

依存関係で libpng とか libjpeg とか入れないといけないかも。

今回は Xcode 用の Framework も作りたかったので、ソースを取ってきて自分でビルド・インストールする方法を取った。

ソースは公式ページからダウンロードできる。

$ wget http://downloads.sourceforge.net/project/opencvlibrary/opencv-unix/2.0/OpenCV-2.0.0.tar.bz2?use_mirror=cdnetworks-kr-1
$ tar xjvf OpenCV-2.0.0.tar.bz2
$ cd OpenCV-2.0.0
$ ./configure

ここまでは問題無し。が、無事 configure が終わったところで、ビルドをしようと

$ make framework

すると、途中でコンパイルが失敗する。どうやら、 QuickTime 周りで問題が発生している様子。

調べてみると、 Snow Leopard からこの問題は発生するようになったようだ。おそらく QuickTime 周りの実装変更(64bit化とか)が原因なのだろうけど…困った。

さらに調べると、 Snow Leopard 上でビルドする方法を紹介してるページを発見。ただし、通常のビルドのみ。

OpenCVをSnow Leopardでコンパイルできた!カメラも動いた!

Via: トライアウト ラボラトリ!

とりあえず、紹介されているとおりにインストール。一応開発環境は整った。

が、 Framework を作っておきたかったので、悪あがきで最新版を svn から取ってきてビルドしてみた。

$ svn co  https://code.ros.org/svn/opencv/trunk/opencv
$ cd opencv
$ ./make_frameworks.sh

Via: http://opencv.willowgarage.com/wiki/Mac_OS_X_OpenCV_Port

…あっさり成功したw

どうやら最新のソースではこの問題はきちんと解決されているようだ。

あとは、 opencv ディレクトリに生成された OpenCV.framework を /Library/Framework ディレクトリに移動すればOK。

Xcode 上からも正常に認識され、プロジェクトに追加できるようになった。

OpenCV を使って画像を読み込む

OpenCV を使った画像の読み込みは至って簡単。

jpeg, png, tiff等の画像ファイルが、たったの数行で読み込める。

  • OpenCV で画像を読み込む
  • // "hogehoge.png" を読み込む
    IplImage *image = cvLoadImage("hogehoge.png", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
    if(image == NULL) {
        // 読み込みに失敗したらエラーを出力して終了
        perror(fname);
        exit(EXIT_FAILURE);
    }
    // 色空間を変換する
    cvCvtColor(image, image, CV_BGR2RGB);
    // 画像を反転
    cvFlip(image, NULL, 0);

読み込まれた画像のヘッダ情報とデータは、 IplImage 構造体として返される。

あとは、 image->width で画像幅、 image->height で画像高、 image->imageData で画像のデータをそれぞれ取得し、テクスチャを生成してやればいい。

読み込んだ画像が必要なくなったら、

cvReleaseImage(IplImage**)

でリソースを開放するのを忘れずに。

OpenGL のテクスチャを生成する

C++ のクラスは Java とは違って多重継承が許される。

練習がてら、継承を使ったテクスチャ生成クラスを定義してみた。

  • ヘッダ
  • /* hoge.h */
    #pragma once
    #include <string>
    #include <GL/glut.h>
    #include <OpenCV/cv.h>
    
    class Texture
    {
    protected:
        GLuint id; // texture id
    public:
        Texture();
        ~Texture();
        GLuint getTextureID(){return id;}
    };
    
    class ILoadImage
    {
    protected:
        char *data; // raw data
        std::string filename;
        GLuint width, height; // texture size
    private:
        virtual void load() = 0; // picture load
    public:
        virtual ~ILoadImage(){}
        GLuint getWidth(){return width;}
        GLuint getHeight(){return height;}
    };
    
    class CvTexture : public Texture, public ILoadImage
    {
    private:
        IplImage *image;
        void init();
        void load();
        void final();
    public:
        CvTexture();
        CvTexture(const char*);
        ~CvTexture(){}
    };
  • ソース
  • #include <hoge.h>
    #include <OpenCV/highgui.h>
    
    /********************************************
     * class Texture
     *
     * Desc: テクスチャクラス
     ********************************************/
    // constructor
    Texture::Texture()
    {
        // テクスチャを1つ生成
        glGenTextures(1, &id);
    }
    // destructor
    Texture::~Texture()
    {
        // テクスチャを破棄
        glDeleteTextures(1, &id);
    }
    
    /********************************************
     * class CvTexture
     *
     * Desc: OpenCVを用いてテクスチャを生成する
     ********************************************/
    CvTexture::CvTexture(const char* fname)
    {
        filename = std::string(fname);
        load();
        init();
        final();
    }
    void CvTexture::load()
    {
        image = cvLoadImage(filename.c_str(),CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
    	if(image == NULL) {
    		perror(filename.c_str());
            exit(EXIT_FAILURE);
    	}
    
    	cvCvtColor(image, image, CV_BGR2RGB);
    	cvFlip(image, NULL, 0);
    
        width = image->width;
        height = image->height;
    }
    void CvTexture::init()
    {
        GLenum format;
        if (image->alphaChannel) {
            format = GL_RGBA;
        } else {
            format = GL_RGB;
        }
    
        glBindTexture(GL_TEXTURE_2D, id);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, width, height, GL_RGB, GL_UNSIGNED_BYTE, image->imageData);
    }
    void CvTexture::final()
    {
        cvReleaseImage(&image);
    }

使うときは、 CvTexture のポインタをグローバル変数、もしくは static な内部変数で定義しておく必要がある。

  • 使用例
  • CvTexture *texture;
    ・・・・
    void initialize()
    {
        ・・・・
        // CvTexture オブジェクト生成・テクスチャ生成
        texture = new CvTexture("hogehoge.png");
        ・・・・
    }
    
    void display()
    {
        ・・・・
        glEnable(GL_TEXTURE_2D);
        // 生成したテクスチャに切り替える
        glBindTexture(GL_TEXTURE_2D, texture->getTextureID());
        ・・・・
    }

でないとコールバックが呼ばれる度にコンストラクタが呼ばれ、オーバーヘッドが酷いことになる。

また、別の関数でオブジェクトを生成する場合は、そうしておかないと別の関数へスコープが移る際にオブジェクトが破棄され、 Texture クラスのデストラクタが呼ばれてしまう。よって、クラスと一緒にテクスチャも破棄される。

未だにどういった場面で、どう継承してやればベストなのか分からない。

…何の計画性もなくコードを書くのを止めたら分かるようになるのかもしれないw