cocos2d-x と OpenCV を一緒に使う環境を作った時のメモ。Android編 を記します。
cocos2d-x 3.4
OpenCV 2.4.10
OpenCVライブラリの入手
最新版をDL
http://opencv.org/downloads.html
2015.02.16 時点で安定板は 2.4.10 “OpenCV for Android” をクリック
OpenCV-2.4.10-android-sdk.zip
テストプロジェクトを作って使ってみる
プロジェクト作成
cocos2d-x Ver3.4をつかいます
ln -s ./cocos2d-x-3.4 ./cocos2d-x
Cococs2d-x プロジェクト生成 HelloOpenCV
cd ./cocos2d-x
cocos new HelloOpenCV -p net.szmake.HelloOpenCV -l cpp -d ~/SpDev/ProjCocos2dxV3.4/
Eclipseから開く
Import > Android > Existing Android Code Into Workspace
ImportProjectダイアログが開く
RootDirectory[Browse…]ボタンからさきほど作成した
~/SpDev/ProjCocos2dxV3.4/HelloOpenCV/proj.android
~/SpDev/rojCocos2dxV3.4/HelloOpenCV/SandBox/cocos2d/cocos/platform/android/java
とりあえず空の状態で初期実行して正常に動く事を確認。(実機でcocos2dx宇宙人が表示される)
テストアプリ:ライブラリコールしてみる
OpenCVライブラリが使えるか確認。
簡単なサンプルとして、CCSpriteを白黒変換して表示してみます。
/HelloOpenCV/proj.android/ の下に
OpenCV-2.4.10-android-sdk.zip
を解凍した
OpenCV-2.4.10-android-sdk ディレクトリをコピー
(EcclipseのPackageExplorerでrefleshすると、HelloOpenCVの配下にOpenCV-2.4.10-android-sdkが出てくる)
Classes/HelloWorldScene.h の編集
行頭でopencv のヘッダーファイルインクルード
1 2 3 |
#include "opencv2/core/core.hpp" #include "opencv2/imgproc/imgproc.hpp" |
テスト確認用のクラス・メソッドを追加
1 2 3 4 5 6 7 |
// OpenCVテスト cv::Mat ccImage2cvMat(cocos2d::Image* ccImage); cv::Mat createCvMatFromRaw(unsigned char *rawData, int rawXW, int rawYW, int ch); cocos2d::Image* cvMat2ccImage(cv::Mat cvMat); // --- 実行トリガラベルクリック時 void onTestLblClick(Ref* pSender); |
Classes/HelloWorldScene.cpp の編集
OpenCV内では画像データはcv::Mat形式で扱われるので
CCSprite<-->cv::Matの 変換ルーチンが必要。下記ルーチンを自作しました。
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
// ccImage2cvMat変換 cv::Mat HelloWorld::ccImage2cvMat(cocos2d::Image* ccImage) { // CCImageから一式情報展開 //総ピクセル数(unsigned int) int imageSize = (int)ccImage->getDataLen(); log("-----imageSize:%d", imageSize); //横ピクセル数(unsigned int) int imageXW = ccImage->getWidth(); //縦ピクセル数(unsigned int) int imageYW = ccImage->getHeight(); //ピクセルデータ(配列) unsigned char * srcData = ccImage->getData(); // (補足) // 3ch-RGB/4ch-RGBA の判断 log("imageXW=%d, imageYW=%d", imageXW, imageYW); int ch = imageSize/(imageXW*imageYW); log("image=%dch raw data...", ch); return this->createCvMatFromRaw(srcData, imageXW, imageYW, ch); } // Raw2cvMat変換 cv::Mat HelloWorld::createCvMatFromRaw(unsigned char *rawData, int rawXW, int rawYW, int ch) { // 展開先のMat用意 cv::Mat cvMat( rawYW, rawXW, CV_8UC4); // 8 bits per component, 4 channels for (int py=0; py<rawYW; py++) { for (int px=0; px<rawXW; px++) { // この座標の画素情報をセット //1ピクセルの情報を取り出す int nBasePos = ((rawXW * py)+px) * ch; cvMat.at<cv::Vec4b>(py, px) = cv::Vec4b(rawData[nBasePos + 0], // 赤 rawData[nBasePos + 1], // 緑 rawData[nBasePos + 2], // 青 0xFF); // Alafa } } return cvMat; } // cvMat2ccImage変換 cocos2d::Image* HelloWorld::cvMat2ccImage(cv::Mat cvMat) { // cvMatを用意するときは常にCV_8UC4(='8 bits per component, 4 channels')の前提とするので考慮の必要なし int elemSize = (int)cvMat.elemSize(); unsigned char *srcData; int width = cvMat.cols; int height = cvMat.rows; //log("***elemSize:%d", elemSize); //log("***width=%d, height=%d", width, height); // initWithRawDataがマルチチャンネル(RGBA8888)以外サポートしていないので変換 unsigned char * pTempData = NULL; if(1==elemSize){ log("gray-mode"); // グレースケール // initWithRawDataがマルチチャンネル(RGBA8888)以外サポートしていないので変換 int bytesPerComponent = 4; long size = bytesPerComponent * width * height; pTempData = static_cast<unsigned char*>(malloc(size * sizeof(unsigned char))); if(pTempData){ int imageYW=height; int imageXW=width; unsigned char * matData = (unsigned char *)cvMat.data; for (int py=0; py<imageYW; py++) { for (int px=0; px<imageXW; px++) { // この座標の画素情報をセット //1ピクセルの情報を取り出す int matPixPos = (imageXW * py * 1) + (px * 1); int pixPos = (imageXW * py * bytesPerComponent) + (px * bytesPerComponent); pTempData[pixPos+0]=matData[matPixPos]; // Red pTempData[pixPos+1]=matData[matPixPos]; // Green pTempData[pixPos+2]=matData[matPixPos]; // Blue pTempData[pixPos+3]=0xFF; // Alfa } } } srcData = pTempData; } else { log("color-mode"); // カラー srcData = (unsigned char *)cvMat.data; } long size = 4 * width * height; // イメージデータ生成 Image* ccImage = new Image(); ccImage->autorelease(); if (ccImage && ccImage->initWithRawData( srcData, size, width, height, elemSize * 8 ) ) { if(pTempData){ free(pTempData); } return ccImage; } return NULL; } |
- HelloWorld::init()関数内にCCSpriteとラベルのボタンを配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// ********************************************* // 変換対象画像 Sprite *pSpriteIView = Sprite::create("app_image.png"); // 2倍表示 pSpriteIView->setScale(2.0f); // スプライトの位置を設定 pSpriteIView->setPosition(Point(visibleSize.width/2 + origin.x, visibleSize.height*60/100 + origin.y)); // 後で識別できるようにtagを設定 pSpriteIView->setTag(1); // スプライトをレイヤに追加 this->addChild(pSpriteIView, 1); // ********************************************* auto lblBtn = LabelTTF::create("白黒になーれ", "Arial", 24); auto linktext_cfg = MenuItemLabel::create(lblBtn, CC_CALLBACK_1(HelloWorld::onTestLblClick, this)); auto menu_linktext_cfg = Menu::create(linktext_cfg, NULL); menu_linktext_cfg->setPosition(Point(visibleSize.width/2 + origin.x, 230 ) ); this->addChild(menu_linktext_cfg, 1); |
ラベルリンク、タップ時のコールバック
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 |
void HelloWorld::onTestLblClick(Ref* pSender) { log("HelloWorld::onTestLblClick"); Image* srcImage = new Image(); if(srcImage->initWithImageFile("app_image.png")){ log("srcImageLoad[OK]"); cv::Mat srcMat = this->ccImage2cvMat(srcImage); cv::Mat greyMat; cv::cvtColor(srcMat, greyMat, CV_BGR2GRAY); Image* grayImage = this->cvMat2ccImage(greyMat); // 表示切り替え Texture2D* texture = new Texture2D(); if (texture && texture->initWithImage(grayImage)) { //texture->autorelease(); Sprite *pSpriteIView = (Sprite *)this->getChildByTag(1); pSpriteIView->setTexture(texture); } //CC_SAFE_DELETE(texture); } else { log("srcImageLoad[NG]"); } } |
Android.mk の編集(重要ポイント)
/HelloOpenCV/proj.android/jni/Android.mk を修正
(編集前)
1 2 3 4 5 6 |
include $(CLEAR_VARS) $(call import-add-path,$(LOCAL_PATH)/../../cocos2d) $(call import-add-path,$(LOCAL_PATH)/../../cocos2d/external) $(call import-add-path,$(LOCAL_PATH)/../../cocos2d/cocos) |
↓
(編集後)
1 2 3 4 5 6 7 8 9 |
include $(CLEAR_VARS) OPENCV_LIB_TYPE:=STATIC include ./OpenCV-2.4.10-android-sdk/sdk/native/jni/OpenCV.mk $(call import-add-path,$(LOCAL_PATH)/../../cocos2d) $(call import-add-path,$(LOCAL_PATH)/../../cocos2d/external) $(call import-add-path,$(LOCAL_PATH)/../../cocos2d/cocos) |
(編集前)
1 2 |
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes |
↓
(編集後)
1 2 3 4 5 6 |
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes OPENCV_INCLUDE_DIR:=$(LOCAL_PATH)/include LOCAL_C_INCLUDES+= $(OPENCV_INCLUDE_DIR) ./OpenCV-2.4.9-android-sdk/sdk/native/jni/include |
Application.mk の編集(重要ポイント)
/HelloOpenCV/proj.android/jni/Application.mk を修正
(編集前)
1 2 |
APP_STL := c++_static |
↓
(編集後)
1 2 |
APP_STL := gnustl_static |
コンパイル成功。
無事動きました。
The code is simple and is essentially straight from this tutorial.
When i build folow this tutorial, i’ve bug
Please, help me fix bug!
Thanks!
jni/include/OpenCV/sdk/native/jni/include/opencv2/core/mat.hpp:125: error: undefined reference to ‘cv::Mat::copySize(cv::Mat const&)’
jni/include/OpenCV/sdk/native/jni/include/opencv2/core/mat.hpp:278: error: undefined reference to ‘cv::fastFree(void*)’
jni/include/OpenCV/sdk/native/jni/include/opencv2/core/mat.hpp:353: error: undefined reference to ‘cv::Mat::create(int, int const*, int)’
jni/include/OpenCV/sdk/native/jni/include/opencv2/core/mat.hpp:367: error: undefined reference to ‘cv::Mat::deallocate()’
jni/../../Classes/HelloWorldScene.cpp:39: error: undefined reference to ‘cv::_OutputArray::_OutputArray(cv::Mat&)’
jni/../../Classes/HelloWorldScene.cpp:39: error: undefined reference to ‘cv::_InputArray::_InputArray(cv::Mat const&)’
jni/../../Classes/HelloWorldScene.cpp:39: error: undefined reference to ‘cv::cvtColor(cv::_InputArray const&, cv::_OutputArray const&, int, int)’
collect2: error: ld returned 1 exit status
make: *** [obj/local/x86/libcocos2dcpp.so] Error 1
make: Leaving directory `/Users/khaipham/Desktop/OpenCV/OpenCV2DX/proj.android’
Error running command, return code: 2.
Traceback (most recent call last):
File “./build_native.py”, line 43, in
build(opts.build_mode)
File “./build_native.py”, line 28, in build
raise Exception(“Build dynamic library for project [ ” + app_android_root + ” ] fails!”)
Exception: Build dynamic library for project [ /Users/khaipham/Desktop/OpenCV/OpenCV2DX/proj.android/../ ] fails!