OpenCVで特定の色領域だけ抜き出すコード(JNI版)
OpenCV for Androidで特定の色領域だけ抜き出すコードを書きました。
OpenCV for AndroidではMatクラスのnative instanceはMat.nativeObjで取得できます。
ここには単純にOpenCV内部のMatクラスへのポインタが格納されているので、えいやっとそいつを取ってきて、あとはゴニョゴニョするだけですね。
処理自体はHSV色空間でやっているので、RGB(A)→HSV→RGBAと変換しています。
具体的には特定範囲の色相で、且つ一定値以上の彩度・明度をもった色を残してあとは黒で塗るということをしています。
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <android/log.h>
#define LOG_TAG "android_log_tag"
#define LOGD(...) (void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__);
static void throw_CvException(JNIEnv *env, cv::Exception &e);
JNIEXPORT void JNICALL Java_jni_class_fqn_colorMask
(JNIEnv *env, jclass clazz, jlong src, jlong dst, jint minHue, jint maxHue, jint minSaturation, jint minValue)
{
try {
cv::Mat const *srcMat = static_cast<cv::Mat const *>(reinterpret_cast<void const *>(src));
cv::Mat *dstMat = static_cast<cv::Mat*>(reinterpret_cast<void *>(dst));
std::vector<cv::Mat> hsv;
cv::Mat hsvMat;
cv::cvtColor(*srcMat, hsvMat, CV_BGR2HSV, 3);
cv::split(hsvMat, hsv);
int const c = srcMat->cols;
int const r = srcMat->rows;
for (int y = 0; y < c; ++y) {
for (int x = 0; x < r; ++x) {
int const h = (hsv[0].at<uint8_t>(x, y) & 0xff) << 1;
bool isMasked = false;
if ((minHue <= h) && (h <= maxHue)) {
int const s = hsv[1].at<uint8_t>(x, y) & 0xff;
if (minSaturation <= s) {
int const v = hsv[2].at<uint8_t>(x, y) & 0xff;
if (minValue <= v) {
isMasked = true;
}
}
}
if (!isMasked) {
hsv[1].at<uint8_t>(x, y) = 0;
hsv[2].at<uint8_t>(x, y) = 0;
}
}
}
cv::Mat merged;
cv::merge(hsv, merged);
cv::cvtColor(merged, *dstMat, CV_HSV2BGR, 4);
} catch (cv::Exception& e) {
LOGD("colorMask() catched cv::Exception: %s", e.what());
throw_CvException(env, e);
}
}
static void throw_exception(JNIEnv *env, char const * const className, char const * const message)
{
jclass cls = env->FindClass(className);
if (!cls) {
cls = env->FindClass("java/lang/Exception");
}
env->ThrowNew(cls, message);
env->DeleteLocalRef(cls);
}
static void throw_CvException(JNIEnv *env, cv::Exception &e)
{
throw_exception(env, "org/opencv/core/CvException", e.what());
}
NDKでbuildするにはopencvのshared libraryが必要なので、こんな感じでAndroid.mkを書いてみました。
LOCAL_PATH := $(call my-dir) OPENCV_PATH := /path/to/opencv OPENCV_LIBS := /path/to/opencv/libs # dummy. # copy libopencv_java.so into "obj/local/armeabi" manually. include $(CLEAR_VARS) LOCAL_MODULE := opencv_java include $(BUILD_SHARED_LIBRARY) # build library include $(CLEAR_VARS) LOCAL_MODULE := your_project LOCAL_CPPFLAGS := -Wall -Werror -O2 LOCAL_SRC_FILES := your_source.cpp LOCAL_LDLIBS += -llog LOCAL_C_INCLUDES := $(OPENCV_PATH)/modules/core/include $(OPENCV_PATH)/modules/imgproc/include LOCAL_SHARED_LIBRARIES := libopencv_java include $(BUILD_SHARED_LIBRARY)
中に書いてあるように、libopencv_java.soをリンクさせるためにdummy projectを作っています。
こんな感じのshell scriptを書いて適当にlibopencv_java.soをコピると良いかと思います。
#!/bin/sh
OPENCV_LIB=/path/to/opencv/libs
mkdir -p ../obj/local/armeabi
mkdir -p ../obj/local/armeabi-v7a
cp $OPENCV_LIB/armeabi/libopencv_java.so ../obj/local/armeabi
cp $OPENCV_LIB/armeabi-v7a/libopencv_java.so ../obj/local/armeabi-v7a
ndk-build
if [ -e ../libs/armeabi/libopencv_java.so ]; then
rm ../libs/armeabi/libopencv_java.so
fi
if [ -e ../libs/armeabi-v7a/libopencv_java.so ]; then
rm ../libs/armeabi-v7a/libopencv_java.so
fi
armeabi-v7aがいらないひとは適当にけずってください。