Android RakNet 系列之五 视频通讯 OpenCV4Android

简介

引入OpenCV4Android的目标是在Raknet框架下解决视频通讯的问题,目前在ubuntu下已成功实现,现在把它引用到Android平台下。

OpenCV是一个基于开源发行的跨平台计算机视觉库,可以在 Windows, Android, Maemo,FreeBSD, OpenBSD, iOS,Linux 和Mac OS等平台上运行。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。OpenCV致力于真实世界的实时应用,通过优化的C代码的编写对其执行速度带来了可观的提升,并且可以通过购买Intel的IPP高性能多媒体函数库(Integrated Performance Primitives)得到更快的处理速度。

相关网站

http://sourceforge.net/projects/opencvlibrary/files/opencv-android/ 最新版项目源码下载
http://www.opencv.org.cn/   OpenCV中文论坛
http://opencv.org/?s=+android&x=0&y=0  OpenCV 官网
http://www.code.opencv.org/projects/opencv/issues OpenCV 官网下解决方案
http://docs.opencv.org/platforms/android/service/doc/index.html 或  http://docs.opencv.org/trunk/     OpenCV 官网下介绍文档

http://www.jayrambhia.com/android/ 相关博客

详情

项目介绍

官方提供了一个库项目(OpenCV Library - 2.4.10)和九个Demo项目(OpenCV Manager - 2.4.10、OpenCV Tutorial 1 - Camera Preview、OpenCV Tutorial 2 - Mixed Processing、OpenCV Tutorial 3 - Camera Control、OpenCV Sample - 4 puzzle、OpenCV Sample - camera-calibration、OpenCV Sample - color-blob-detection、OpenCV Sample - face-detection、OpenCV Sample - image-manipulations、OpenCV Sample - native-activity、OpenCV Library - 2.4.10),再加上OpenCV Manager项目共计11个项目,项目如图:




OpenCV Manager - 2.4.10

官方提供的Demo运行需要OpenCV Manager项目的支持,该项目负责匹配手机加载库文件,通过服务绑定进行通讯。

如果未安装它,并运行Demo会出现提示框:OpenCV Manager package was not found! Try to install it?  当然如果直接使用它的库运行是不会出现此情况的。原因就是该项目只是做加载库的工作。


项目如图:



官方给出解释:




加载库的代码如下:

public class BinderConnector {
	private static boolean mIsReady = false;
	private MarketConnector mMarket;

	static {
		try {
			System.loadLibrary("OpenCVEngine");
			System.loadLibrary("OpenCVEngine_jni");
			mIsReady = true;
		} catch (UnsatisfiedLinkError localUnsatisfiedLinkError) {
			mIsReady = false;
			localUnsatisfiedLinkError.printStackTrace();
		}
	}

	public BinderConnector(MarketConnector paramMarketConnector) {
		this.mMarket = paramMarketConnector;
	}

	private native void Final();

	private native boolean Init(MarketConnector paramMarketConnector);

	public native IBinder Connect();

	public boolean Disconnect() {
		if (mIsReady)
			Final();
		return mIsReady;
	}

	public boolean Init() {
		boolean bool = false;
		if (mIsReady)
			bool = Init(this.mMarket);
		return bool;
	}
}
public class HardwareDetector { //硬件匹配
	public static final int ARCH_ARMv5 = 0x4000000;
	public static final int ARCH_ARMv6 = 0x8000000;
	public static final int ARCH_ARMv7 = 0x10000000;
	public static final int ARCH_ARMv8 = 0x20000000;
	public static final int ARCH_MIPS = 0x40000000;
	public static final int ARCH_UNKNOWN = -1;
	public static final int ARCH_X64 = 0x2000000;
	public static final int ARCH_X86 = 0x1000000;
	public static final int FEATURES_HAS_GPU = 65536;
	public static final int FEATURES_HAS_NEON = 8;
	public static final int FEATURES_HAS_NEON2 = 22;
	public static final int FEATURES_HAS_SSE = 1;
	public static final int FEATURES_HAS_SSE2 = 2;
	public static final int FEATURES_HAS_SSE3 = 4;
	public static final int FEATURES_HAS_VFPv3 = 2;
	public static final int FEATURES_HAS_VFPv3d16 = 1;
	public static final int FEATURES_HAS_VFPv4 = 4;
	public static final int PLATFORM_TEGRA = 1;
	public static final int PLATFORM_TEGRA2 = 2;
	public static final int PLATFORM_TEGRA3 = 3;
	public static final int PLATFORM_TEGRA4 = 5;
	public static final int PLATFORM_TEGRA4i = 4;
	public static final int PLATFORM_TEGRA5 = 6;
	public static final int PLATFORM_UNKNOWN = -1;
	public static boolean mIsReady = false;

	static {
		try {
			System.loadLibrary("OpenCVEngine");
			System.loadLibrary("OpenCVEngine_jni");
			mIsReady = true;
		} catch (UnsatisfiedLinkError localUnsatisfiedLinkError) {
			mIsReady = false;
			localUnsatisfiedLinkError.printStackTrace();
		}
	}

	public static native int DetectKnownPlatforms();

	public static native int GetCpuID();

	public static native String GetPlatformName();

	public static native int GetProcessorCount();
}
public class OpenCVLibraryInfo {
	private String mLibraryList;
	private long mNativeObj;
	private String mPackageName;
	private String mVersionName;

	public OpenCVLibraryInfo(String paramString) {
		mNativeObj = open(paramString + "/libopencv_info.so");

		if (this.mNativeObj == 0L)
			return;
		this.mPackageName = getPackageName(this.mNativeObj);
		this.mLibraryList = getLibraryList(this.mNativeObj);
		this.mVersionName = getVersionName(this.mNativeObj);
		close(this.mNativeObj);
	}

	private native void close(long paramLong);

	private native String getLibraryList(long paramLong);

	private native String getPackageName(long paramLong);

	private native String getVersionName(long paramLong);

	private native long open(String paramString);

	public String libraryList() {
		return this.mLibraryList;
	}

	public String packageName() {
		return this.mPackageName;
	}

	public boolean status() {
		if (mNativeObj == 0L || mLibraryList.length() == 0)
			return false;

		return true;
	}

	public String versionName() {
		return this.mVersionName;
	}
}
//关键服务  demo中绑定的服务就该服务 该服务就是匹配手机加载库
public class OpenCVEngineService extends Service {
	private static final String TAG = "OpenCVEngine/Service";
	private IBinder mEngineInterface = null;
	private MarketConnector mMarket;
	private BinderConnector mNativeBinder;

	public void OnDestroy() {
		Log.i("OpenCVEngine/Service", "OpenCV Engine service destruction");
		this.mNativeBinder.Disconnect();
	}

	public IBinder onBind(Intent paramIntent) {
		Log.i("OpenCVEngine/Service", "Service onBind called for intent "
				+ paramIntent.toString());
		return this.mEngineInterface;
	}

	public void onCreate() {
		Log.i("OpenCVEngine/Service", "Service starting");
		super.onCreate();
		Log.i("OpenCVEngine/Service", "Engine binder component creating");
		this.mMarket = new MarketConnector(getBaseContext());
		this.mNativeBinder = new BinderConnector(this.mMarket);
		if (this.mNativeBinder.Init()) {
			this.mEngineInterface = this.mNativeBinder.Connect();
			Log.i("OpenCVEngine/Service", "Service started successfully");
		}
		else {
			 Log.e("OpenCVEngine/Service",
			 "Cannot initialize native part of OpenCV Manager!");
			 Log.e("OpenCVEngine/Service", "Using stub instead");
			return;
		}
		
		this.mEngineInterface = new OpenCVEngineInterface(this);
	}

	public boolean onUnbind(Intent paramIntent) {
		Log.i("OpenCVEngine/Service", "Service onUnbind called for intent "
				+ paramIntent.toString());
		return true;
	}
}
它的AndroidManifest.xml 如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest android:versionCode="2191" android:versionName="2.19" package="org.opencv.engine"
  xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
    <application android:label="@string/app_name" android:icon="@drawable/icon">
        <service android:name="OpenCVEngineService" android:exported="true" android:process=":OpenCVEngineProcess">
            <intent-filter>
                <action android:name="org.opencv.engine.BIND" />
            </intent-filter>
        </service>
        <activity android:label="@string/app_name" android:name="org.opencv.engine.manager.ManagerActivity" android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>


OpenCV Tutorial 1 - Camera Preview

Demo就一个文件,使用摄像机预览,Demo中提供了两种方法,一种是Java层调用,一种是Native层调用。

如图:


关键控件如下:

    <org.opencv.android.JavaCameraView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:visibility="gone"
        android:id="@+id/tutorial1_activity_java_surface_view"
        opencv:show_fps="true"
        opencv:camera_id="any" />

    <org.opencv.android.NativeCameraView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:visibility="gone"
        android:id="@+id/tutorial1_activity_native_surface_view"
        opencv:show_fps="true"
        opencv:camera_id="any" />


OpenCV Tutorial 2 - Mixed Processing

该Demo就一个Java文件和一个C++文件,实现4中混合处理。

如图:


本地代码如下:

JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial2_Tutorial2Activity_FindFeatures(JNIEnv*, jobject, jlong addrGray, jlong addrRgba)
{
    Mat& mGr  = *(Mat*)addrGray;
    Mat& mRgb = *(Mat*)addrRgba;
    vector<KeyPoint> v;

    FastFeatureDetector detector(50);
    detector.detect(mGr, v);
    for( unsigned int i = 0; i < v.size(); i++ )
    {
        const KeyPoint& kp = v[i];
        circle(mRgb, Point(kp.pt.x, kp.pt.y), 10, Scalar(255,0,0,255));
    }
}

使用的控件如下:

    <org.opencv.android.JavaCameraView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/tutorial2_activity_surface_view" />


OpenCV Tutorial 3 - Camera Control

该Demo就2个Java文件,摄像头控制,控制颜色显示、摄像分辨率。

效果如图:


关键代码:

使用的控件

    <org.opencv.samples.tutorial3.Tutorial3View
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone"
        android:id="@+id/tutorial3_activity_java_surface_view" />
public class Tutorial3View extends JavaCameraView implements PictureCallback {

    private static final String TAG = "Sample::Tutorial3View";
    private String mPictureFileName;

    public Tutorial3View(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public List<String> getEffectList() {
        return mCamera.getParameters().getSupportedColorEffects();
    }

    public boolean isEffectSupported() {
        return (mCamera.getParameters().getColorEffect() != null);
    }

    public String getEffect() {
        return mCamera.getParameters().getColorEffect();
    }

    public void setEffect(String effect) {
        Camera.Parameters params = mCamera.getParameters();
        params.setColorEffect(effect);
        mCamera.setParameters(params);
    }

    public List<Size> getResolutionList() {
        return mCamera.getParameters().getSupportedPreviewSizes();
    }

    public void setResolution(Size resolution) {
        disconnectCamera();
        mMaxHeight = resolution.height;
        mMaxWidth = resolution.width;
        connectCamera(getWidth(), getHeight());
    }

    public Size getResolution() {
        return mCamera.getParameters().getPreviewSize();
    }

    public void takePicture(final String fileName) {
        Log.i(TAG, "Taking picture");
        this.mPictureFileName = fileName;
        // Postview and jpeg are sent in the same buffers if the queue is not empty when performing a capture.
        // Clear up buffers to avoid mCamera.takePicture to be stuck because of a memory issue
        mCamera.setPreviewCallback(null);

        // PictureCallback is implemented by the current class
        mCamera.takePicture(null, null, this);
    }

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        Log.i(TAG, "Saving a bitmap to file");
        // The camera preview was automatically stopped. Start it again.
        mCamera.startPreview();
        mCamera.setPreviewCallback(this);

        // Write the image in a file (in jpeg format)
        try {
            FileOutputStream fos = new FileOutputStream(mPictureFileName);

            fos.write(data);
            fos.close();

        } catch (java.io.IOException e) {
            Log.e("PictureDemo", "Exception in photoCallback", e);
        }

    }
}


OpenCV Sample - 4 puzzle

该Demo就两个Java文件,实现摄像格式错乱输出。

效果如图:


主要代码如下:

public class Puzzle15Processor { //摄像图片转换

    private static final int GRID_SIZE = 4;
    private static final int GRID_AREA = GRID_SIZE * GRID_SIZE;
    private static final int GRID_EMPTY_INDEX = GRID_AREA - 1;
    private static final String TAG = "Puzzle15Processor";
    private static final Scalar GRID_EMPTY_COLOR = new Scalar(0x33, 0x33, 0x33, 0xFF);

    private int[]   mIndexes;
    private int[]   mTextWidths;
    private int[]   mTextHeights;

    private Mat mRgba15;
    private Mat[] mCells15;
    private boolean mShowTileNumbers = true;

    public Puzzle15Processor() {
        mTextWidths = new int[GRID_AREA];
        mTextHeights = new int[GRID_AREA];

        mIndexes = new int [GRID_AREA];

        for (int i = 0; i < GRID_AREA; i++)
            mIndexes[i] = i;
    }

    /* this method is intended to make processor prepared for a new game */
    public synchronized void prepareNewGame() {
        do {
            shuffle(mIndexes);
        } while (!isPuzzleSolvable());
    }

    /* This method is to make the processor know the size of the frames that
     * will be delivered via puzzleFrame.
     * If the frames will be different size - then the result is unpredictable
     */
    public synchronized void prepareGameSize(int width, int height) {
        mRgba15 = new Mat(height, width, CvType.CV_8UC4);
        mCells15 = new Mat[GRID_AREA];

        for (int i = 0; i < GRID_SIZE; i++) {
            for (int j = 0; j < GRID_SIZE; j++) {
                int k = i * GRID_SIZE + j;
                mCells15[k] = mRgba15.submat(i * height / GRID_SIZE, (i + 1) * height / GRID_SIZE, j * width / GRID_SIZE, (j + 1) * width / GRID_SIZE);
            }
        }

        for (int i = 0; i < GRID_AREA; i++) {
            Size s = Core.getTextSize(Integer.toString(i + 1), 3/* CV_FONT_HERSHEY_COMPLEX */, 1, 2, null);
            mTextHeights[i] = (int) s.height;
            mTextWidths[i] = (int) s.width;
        }
    }

    /* this method to be called from the outside. it processes the frame and shuffles
     * the tiles as specified by mIndexes array
     */
    public synchronized Mat puzzleFrame(Mat inputPicture) {
        Mat[] cells = new Mat[GRID_AREA];
        int rows = inputPicture.rows();
        int cols = inputPicture.cols();

        rows = rows - rows%4;
        cols = cols - cols%4;

        for (int i = 0; i < GRID_SIZE; i++) {
            for (int j = 0; j < GRID_SIZE; j++) {
                int k = i * GRID_SIZE + j;
                cells[k] = inputPicture.submat(i * inputPicture.rows() / GRID_SIZE, (i + 1) * inputPicture.rows() / GRID_SIZE, j * inputPicture.cols()/ GRID_SIZE, (j + 1) * inputPicture.cols() / GRID_SIZE);
            }
        }

        rows = rows - rows%4;
        cols = cols - cols%4;

        // copy shuffled tiles
        for (int i = 0; i < GRID_AREA; i++) {
            int idx = mIndexes[i];
            if (idx == GRID_EMPTY_INDEX)
                mCells15[i].setTo(GRID_EMPTY_COLOR);
            else {
                cells[idx].copyTo(mCells15[i]);
                if (mShowTileNumbers) {
                    Core.putText(mCells15[i], Integer.toString(1 + idx), new Point((cols / GRID_SIZE - mTextWidths[idx]) / 2,
                            (rows / GRID_SIZE + mTextHeights[idx]) / 2), 3/* CV_FONT_HERSHEY_COMPLEX */, 1, new Scalar(255, 0, 0, 255), 2);
                }
            }
        }

        for (int i = 0; i < GRID_AREA; i++)
            cells[i].release();

        drawGrid(cols, rows, mRgba15);

        return mRgba15;
    }

    public void toggleTileNumbers() {
        mShowTileNumbers = !mShowTileNumbers;
    }

    public void deliverTouchEvent(int x, int y) {
        int rows = mRgba15.rows();
        int cols = mRgba15.cols();

        int row = (int) Math.floor(y * GRID_SIZE / rows);
        int col = (int) Math.floor(x * GRID_SIZE / cols);

        if (row < 0 || row >= GRID_SIZE || col < 0 || col >= GRID_SIZE) {
            Log.e(TAG, "It is not expected to get touch event outside of picture");
            return ;
        }

        int idx = row * GRID_SIZE + col;
        int idxtoswap = -1;

        // left
        if (idxtoswap < 0 && col > 0)
            if (mIndexes[idx - 1] == GRID_EMPTY_INDEX)
                idxtoswap = idx - 1;
        // right
        if (idxtoswap < 0 && col < GRID_SIZE - 1)
            if (mIndexes[idx + 1] == GRID_EMPTY_INDEX)
                idxtoswap = idx + 1;
        // top
        if (idxtoswap < 0 && row > 0)
            if (mIndexes[idx - GRID_SIZE] == GRID_EMPTY_INDEX)
                idxtoswap = idx - GRID_SIZE;
        // bottom
        if (idxtoswap < 0 && row < GRID_SIZE - 1)
            if (mIndexes[idx + GRID_SIZE] == GRID_EMPTY_INDEX)
                idxtoswap = idx + GRID_SIZE;

        // swap
        if (idxtoswap >= 0) {
            synchronized (this) {
                int touched = mIndexes[idx];
                mIndexes[idx] = mIndexes[idxtoswap];
                mIndexes[idxtoswap] = touched;
            }
        }
    }

    private void drawGrid(int cols, int rows, Mat drawMat) {
        for (int i = 1; i < GRID_SIZE; i++) {
            Core.line(drawMat, new Point(0, i * rows / GRID_SIZE), new Point(cols, i * rows / GRID_SIZE), new Scalar(0, 255, 0, 255), 3);
            Core.line(drawMat, new Point(i * cols / GRID_SIZE, 0), new Point(i * cols / GRID_SIZE, rows), new Scalar(0, 255, 0, 255), 3);
        }
    }

    private static void shuffle(int[] array) {
        for (int i = array.length; i > 1; i--) {
            int temp = array[i - 1];
            int randIx = (int) (Math.random() * i);
            array[i - 1] = array[randIx];
            array[randIx] = temp;
        }
    }

    private boolean isPuzzleSolvable() {

        int sum = 0;
        for (int i = 0; i < GRID_AREA; i++) {
            if (mIndexes[i] == GRID_EMPTY_INDEX)
                sum += (i / GRID_SIZE) + 1;
            else {
                int smaller = 0;
                for (int j = i + 1; j < GRID_AREA; j++) {
                    if (mIndexes[j] < mIndexes[i])
                        smaller++;
                }
                sum += smaller;
            }
        }
        return sum % 2 == 0;
    }
}


OpenCV Sample - camera-calibration

该Demo就4个Java文件,实现摄像校准。

效果如图:



使用的控件:

    <org.opencv.android.JavaCameraView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/camera_calibration_java_surface_view" />


OpenCV Sample - color-blob-detection
该Demo就两个Java文件,实现色斑检测。

效果如图:



使用的控件:

    <org.opencv.android.JavaCameraView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/color_blob_detection_activity_surface_view" />
public class ColorBlobDetector { //匹配
    // Lower and Upper bounds for range checking in HSV color space
    private Scalar mLowerBound = new Scalar(0);
    private Scalar mUpperBound = new Scalar(0);
    // Minimum contour area in percent for contours filtering
    private static double mMinContourArea = 0.1;
    // Color radius for range checking in HSV color space
    private Scalar mColorRadius = new Scalar(25,50,50,0);
    private Mat mSpectrum = new Mat();
    private List<MatOfPoint> mContours = new ArrayList<MatOfPoint>();

    // Cache
    Mat mPyrDownMat = new Mat();
    Mat mHsvMat = new Mat();
    Mat mMask = new Mat();
    Mat mDilatedMask = new Mat();
    Mat mHierarchy = new Mat();

    public void setColorRadius(Scalar radius) {
        mColorRadius = radius;
    }

    public void setHsvColor(Scalar hsvColor) {
        double minH = (hsvColor.val[0] >= mColorRadius.val[0]) ? hsvColor.val[0]-mColorRadius.val[0] : 0;
        double maxH = (hsvColor.val[0]+mColorRadius.val[0] <= 255) ? hsvColor.val[0]+mColorRadius.val[0] : 255;

        mLowerBound.val[0] = minH;
        mUpperBound.val[0] = maxH;

        mLowerBound.val[1] = hsvColor.val[1] - mColorRadius.val[1];
        mUpperBound.val[1] = hsvColor.val[1] + mColorRadius.val[1];

        mLowerBound.val[2] = hsvColor.val[2] - mColorRadius.val[2];
        mUpperBound.val[2] = hsvColor.val[2] + mColorRadius.val[2];

        mLowerBound.val[3] = 0;
        mUpperBound.val[3] = 255;

        Mat spectrumHsv = new Mat(1, (int)(maxH-minH), CvType.CV_8UC3);

        for (int j = 0; j < maxH-minH; j++) {
            byte[] tmp = {(byte)(minH+j), (byte)255, (byte)255};
            spectrumHsv.put(0, j, tmp);
        }

        Imgproc.cvtColor(spectrumHsv, mSpectrum, Imgproc.COLOR_HSV2RGB_FULL, 4);
    }

    public Mat getSpectrum() {
        return mSpectrum;
    }

    public void setMinContourArea(double area) {
        mMinContourArea = area;
    }

    public void process(Mat rgbaImage) {
        Imgproc.pyrDown(rgbaImage, mPyrDownMat);
        Imgproc.pyrDown(mPyrDownMat, mPyrDownMat);

        Imgproc.cvtColor(mPyrDownMat, mHsvMat, Imgproc.COLOR_RGB2HSV_FULL);

        Core.inRange(mHsvMat, mLowerBound, mUpperBound, mMask);
        Imgproc.dilate(mMask, mDilatedMask, new Mat());

        List<MatOfPoint> contours = new ArrayList<MatOfPoint>();

        Imgproc.findContours(mDilatedMask, contours, mHierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

        // Find max contour area
        double maxArea = 0;
        Iterator<MatOfPoint> each = contours.iterator();
        while (each.hasNext()) {
            MatOfPoint wrapper = each.next();
            double area = Imgproc.contourArea(wrapper);
            if (area > maxArea)
                maxArea = area;
        }

        // Filter contours by area and resize to fit the original image size
        mContours.clear();
        each = contours.iterator();
        while (each.hasNext()) {
            MatOfPoint contour = each.next();
            if (Imgproc.contourArea(contour) > mMinContourArea*maxArea) {
                Core.multiply(contour, new Scalar(4,4), contour);
                mContours.add(contour);
            }
        }
    }

    public List<MatOfPoint> getContours() {
        return mContours;
    }
}


OpenCV Sample - face-detection
该Demo就两个Java文件和两个C++文件,实现匹配脸。

效果如图:


使用的控件:

    <org.opencv.android.JavaCameraView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/fd_activity_surface_view" />
public class DetectionBasedTracker
{
    public DetectionBasedTracker(String cascadeName, int minFaceSize) {
        mNativeObj = nativeCreateObject(cascadeName, minFaceSize);
    }

    public void start() {
        nativeStart(mNativeObj);
    }

    public void stop() {
        nativeStop(mNativeObj);
    }

    public void setMinFaceSize(int size) {
        nativeSetFaceSize(mNativeObj, size);
    }

    public void detect(Mat imageGray, MatOfRect faces) {
        nativeDetect(mNativeObj, imageGray.getNativeObjAddr(), faces.getNativeObjAddr());
    }

    public void release() {
        nativeDestroyObject(mNativeObj);
        mNativeObj = 0;
    }

    private long mNativeObj = 0;

    private static native long nativeCreateObject(String cascadeName, int minFaceSize);
    private static native void nativeDestroyObject(long thiz);
    private static native void nativeStart(long thiz);
    private static native void nativeStop(long thiz);
    private static native void nativeSetFaceSize(long thiz, int size);
    private static native void nativeDetect(long thiz, long inputImage, long faces);
}

OpenCV Sample - image-manipulations
该Demo就一个Java文件,实现图片操作。

效果如图:


使用的控件:

    <org.opencv.android.JavaCameraView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/image_manipulations_activity_surface_view" />
实现CvCameraViewListener2接口,从而操作图片。

OpenCV Sample - native-activity

该Demo就一个Java文件和一个C++文件,实现本地界面显示图片。

效果如图:


本地代码如下:

struct Engine
{
    android_app* app;
    cv::Ptr<cv::VideoCapture> capture;
};

static cv::Size calc_optimal_camera_resolution(const char* supported, int width, int height)
{
    int frame_width = 0;
    int frame_height = 0;

    size_t prev_idx = 0;
    size_t idx = 0;
    float min_diff = FLT_MAX;

    do
    {
        int tmp_width;
        int tmp_height;

        prev_idx = idx;
        while ((supported[idx] != ‘\0‘) && (supported[idx] != ‘,‘))
            idx++;

        sscanf(&supported[prev_idx], "%dx%d", &tmp_width, &tmp_height);

        int w_diff = width - tmp_width;
        int h_diff = height - tmp_height;
        if ((h_diff >= 0) && (w_diff >= 0))
        {
            if ((h_diff <= min_diff) && (tmp_height <= 720))
            {
                frame_width = tmp_width;
                frame_height = tmp_height;
                min_diff = h_diff;
            }
        }

        idx++; // to skip comma symbol

    } while(supported[idx-1] != ‘\0‘);

    return cv::Size(frame_width, frame_height);
}

static void engine_draw_frame(Engine* engine, const cv::Mat& frame)
{
    if (engine->app->window == NULL)
        return; // No window.

    ANativeWindow_Buffer buffer;
    if (ANativeWindow_lock(engine->app->window, &buffer, NULL) < 0)
    {
        LOGW("Unable to lock window buffer");
        return;
    }

    int32_t* pixels = (int32_t*)buffer.bits;

    int left_indent = (buffer.width-frame.cols)/2;
    int top_indent = (buffer.height-frame.rows)/2;

    if (top_indent > 0)
    {
        memset(pixels, 0, top_indent*buffer.stride*sizeof(int32_t));
        pixels += top_indent*buffer.stride;
    }

    for (int yy = 0; yy < frame.rows; yy++)
    {
        if (left_indent > 0)
        {
            memset(pixels, 0, left_indent*sizeof(int32_t));
            memset(pixels+left_indent+frame.cols, 0, (buffer.stride-frame.cols-left_indent)*sizeof(int32_t));
        }
        int32_t* line = pixels + left_indent;
        size_t line_size = frame.cols*4*sizeof(unsigned char);
        memcpy(line, frame.ptr<unsigned char>(yy), line_size);
        // go to next line
        pixels += buffer.stride;
    }
    ANativeWindow_unlockAndPost(engine->app->window);
}

static void engine_handle_cmd(android_app* app, int32_t cmd)
{
    Engine* engine = (Engine*)app->userData;
    switch (cmd)
    {
        case APP_CMD_INIT_WINDOW:
            if (app->window != NULL)
            {
                LOGI("APP_CMD_INIT_WINDOW");

                engine->capture = new cv::VideoCapture(0);

                union {double prop; const char* name;} u;
                u.prop = engine->capture->get(CV_CAP_PROP_SUPPORTED_PREVIEW_SIZES_STRING);

                int view_width = ANativeWindow_getWidth(app->window);
                int view_height = ANativeWindow_getHeight(app->window);

                cv::Size camera_resolution;
                if (u.name)
                    camera_resolution = calc_optimal_camera_resolution(u.name, 640, 480);
                else
                {
                    LOGE("Cannot get supported camera camera_resolutions");
                    camera_resolution = cv::Size(ANativeWindow_getWidth(app->window),
                                          ANativeWindow_getHeight(app->window));
                }

                if ((camera_resolution.width != 0) && (camera_resolution.height != 0))
                {
                    engine->capture->set(CV_CAP_PROP_FRAME_WIDTH, camera_resolution.width);
                    engine->capture->set(CV_CAP_PROP_FRAME_HEIGHT, camera_resolution.height);
                }

                float scale = std::min((float)view_width/camera_resolution.width,
                                       (float)view_height/camera_resolution.height);

                if (ANativeWindow_setBuffersGeometry(app->window, (int)(view_width/scale),
                    int(view_height/scale), WINDOW_FORMAT_RGBA_8888) < 0)
                {
                    LOGE("Cannot set pixel format!");
                    return;
                }

                LOGI("Camera initialized at resolution %dx%d", camera_resolution.width, camera_resolution.height);
            }
            break;
        case APP_CMD_TERM_WINDOW:
            LOGI("APP_CMD_TERM_WINDOW");

            engine->capture->release();
            break;
    }
}

void android_main(android_app* app)
{
    Engine engine;

    // Make sure glue isn‘t stripped.
    app_dummy();

    size_t engine_size = sizeof(engine); // for Eclipse CDT parser
    memset((void*)&engine, 0, engine_size);
    app->userData = &engine;
    app->onAppCmd = engine_handle_cmd;
    engine.app = app;

    float fps = 0;
    cv::Mat drawing_frame;
    std::queue<int64> time_queue;

    // loop waiting for stuff to do.
    while (1)
    {
        // Read all pending events.
        int ident;
        int events;
        android_poll_source* source;

        // Process system events
        while ((ident=ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0)
        {
            // Process this event.
            if (source != NULL)
            {
                source->process(app, source);
            }

            // Check if we are exiting.
            if (app->destroyRequested != 0)
            {
                LOGI("Engine thread destroy requested!");
                return;
            }
        }

        int64 then;
        int64 now = cv::getTickCount();
        time_queue.push(now);

        // Capture frame from camera and draw it
        if (!engine.capture.empty())
        {
            if (engine.capture->grab())
                engine.capture->retrieve(drawing_frame, CV_CAP_ANDROID_COLOR_FRAME_RGBA);

             char buffer[256];
             sprintf(buffer, "Display performance: %dx%d @ %.3f", drawing_frame.cols, drawing_frame.rows, fps);
             cv::putText(drawing_frame, std::string(buffer), cv::Point(8,64),
                         cv::FONT_HERSHEY_COMPLEX_SMALL, 1, cv::Scalar(0,255,0,255));
             engine_draw_frame(&engine, drawing_frame);
        }

        if (time_queue.size() >= 2)
            then = time_queue.front();
        else
            then = 0;

        if (time_queue.size() >= 25)
            time_queue.pop();

        fps = time_queue.size() * (float)cv::getTickFrequency() / (now-then);
    }
}

OpenCV Library - 2.4.10
该项目是OpenCV Java SDK,实现Java下操作OpenCV。

如图:


概括如下:

描述
org.opencv.android

对应:
libopencv_androidcamera.a
AsyncServiceHelper:辅助工具:绑定服务、解开服务、加载库。
BaseLoaderCallback:实现加载回调。
CameraBridgeViewBase:摄像控件基类,继承SurfaceView。
FpsMeter:帧统计。
InstallCallbackInterface:初始化回调接口。
JavaCameraView:java层实现摄像。
LoaderCallbackInterface:加载回调接口。
NativeCameraView:本地摄像控件。
OpenCVLoader:根据版本加载OpenCV库。
StaticHelper:辅助OpenCVLoader。
Utils:其他功能辅助。
org.opencv.calib3d

对应:
libopencv_calib3d.a
3D实现库,还包括了BM块匹配算法类StereoBM、SGBM块匹配算法类StereoSGBM类。
org.opencv.contrib

对应:
libopencv_contrib.a
Contrib:
FaceRecognizer:
StereoVar:
org.opencv.core


对应:
libopencv_core.a
核心库 定义了图像的新容器。
Algorithm:
Core:
CvException:
CvType:
Mat:
MatOfByte:
MatOfDMatch:
MatOfFloat:
MatOfDouble:
MatOfFloat4:
MatOfFloat6:
MatOfInt:
MatOfInt4:
MatOfKeyPoint:
MatOfPoint:
MatOfPoint2f:
MatOfPoint3:
MatOfPoint3f:
MatOfRect:
Point:
Point3:
Range:
Rect:
RotatedRect:
Scalar:
Size:
TermCriteria:
org.opencv.engineOpenCVEngineInterface.aidl:
org.opencv.features2d


对应:
libopencv_features2d.a
DescriptorExtractor:
DescriptorMatcher:
FeatureDetector:
DMatch:
Features2d:
GenericDescriptorMatcher:
KeyPoint:
org.opencv.gpu

对应:
libopencv_ocl.a
DeviceInfo:
Gpu:
TargetArchs:
org.opencv.highgui

对应:
libopencv_highgui.a
Highgui:
VideoCapture:
org.opencv.imgproc

对应:
libopencv_imgproc.a
CLAHE:
Imgproc:
Moments:
Subdiv2D:
org.opencv.ml


对应:
libopencv_ml.a
CvANN_MLP_TrainParams:
CvANN_MLP:
CvBoost:
CvBoostParams:
CvDTree:
CvDTreeParams:
CvERTrees:
CvGBTrees:
CvGBTreesParams:
CvKNearest:
CvNormalBayesClassifier:
CvParamGrid:
CvRTParams:
CvRTrees:
CvStatModel:
CvSVM:
CvSVMParams:
EM:
Ml:
org.opencv.objdetect

对应:
libopencv_objdetect.a
CascadeClassifier:
HOGDescriptor:
Objdetect:
org.opencv.photo

对应:
libopencv_photo.a
Photo:
org.opencv.utilsConverters:
org.opencv.video

对应:
libopencv_video.a
libopencv_videostab.a
BackgroundSubtractor:
BackgroundSubtractorMOG:
BackgroundSubtractorMOG2:
KalmanFilter:
Video:

其他模块:

libopencv_flann.a     //特征匹配算法实现 
libopencv_legacy.a
libopencv_stitching.a  //图像拼接
libopencv_superres.a
libopencv_ts.a

OpenCV.mk

 

#编译前 需定义 NDK_USE_CYGPATH=1
USER_LOCAL_PATH:=$(LOCAL_PATH)	#路径


USER_LOCAL_C_INCLUDES:=$(LOCAL_C_INCLUDES)	#定义变量
USER_LOCAL_CFLAGS:=$(LOCAL_CFLAGS)
USER_LOCAL_STATIC_LIBRARIES:=$(LOCAL_STATIC_LIBRARIES)
USER_LOCAL_SHARED_LIBRARIES:=$(LOCAL_SHARED_LIBRARIES)
USER_LOCAL_LDLIBS:=$(LOCAL_LDLIBS)


LOCAL_PATH:=$(subst ?,,$(firstword ?$(subst \, ,$(subst /, ,$(call my-dir))))) #本地路径


OPENCV_TARGET_ARCH_ABI:=$(TARGET_ARCH_ABI)	#目标版本
OPENCV_THIS_DIR:=$(patsubst $(LOCAL_PATH)\\%,%,$(patsubst $(LOCAL_PATH)/%,%,$(call my-dir)))	#本目录
OPENCV_MK_DIR:=$(dir $(lastword $(MAKEFILE_LIST)))	#所有的mk目录
OPENCV_LIBS_DIR:=$(OPENCV_THIS_DIR)/libs/opencv/$(OPENCV_TARGET_ARCH_ABI)	#对应库目录
OPENCV_3RDPARTY_LIBS_DIR:=$(OPENCV_THIS_DIR)/libs/3rdparty/$(OPENCV_TARGET_ARCH_ABI)	#3d库目录
OPENCV_BASEDIR:=		#基目录
OPENCV_LOCAL_C_INCLUDES:="$(LOCAL_PATH)/$(OPENCV_THIS_DIR)/include/opencv" "$(LOCAL_PATH)/$(OPENCV_THIS_DIR)/include" #本地包含目录
OPENCV_MODULES:=contrib legacy stitching superres ocl objdetect ml ts videostab video photo calib3d features2d highgui imgproc flann androidcamera core #对应的库文件
OPENCV_LIB_TYPE:=STATIC	#静态库


OPENCV_HAVE_GPU_MODULE:=off  #是否具备gpu
OPENCV_USE_GPU_MODULE:=on	#是否开启


ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)  #与v7a相等
    ifeq ($(OPENCV_HAVE_GPU_MODULE),on)	#开启gpu
        ifneq ($(CUDA_TOOLKIT_DIR),)
            OPENCV_USE_GPU_MODULE:=on
        endif
    endif
    OPENCV_DYNAMICUDA_MODULE:=
else
    OPENCV_DYNAMICUDA_MODULE:=
endif


CUDA_RUNTIME_LIBS:=


ifeq ($(OPENCV_LIB_TYPE),)	#未表明使用库类型则使用动态
    OPENCV_LIB_TYPE:=SHARED
endif


ifeq ($(OPENCV_LIB_TYPE),SHARED)
    OPENCV_LIBS:=java
    OPENCV_LIB_TYPE:=SHARED
else
    OPENCV_LIBS:=$(OPENCV_MODULES)
    OPENCV_LIB_TYPE:=STATIC
endif


ifeq ($(OPENCV_LIB_TYPE),SHARED) #引入3D库
    OPENCV_3RDPARTY_COMPONENTS:=
    OPENCV_EXTRA_COMPONENTS:=
else
    ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
        OPENCV_3RDPARTY_COMPONENTS:=tbb libjpeg libpng libtiff libjasper IlmImf
        OPENCV_EXTRA_COMPONENTS:=c log m dl z
    endif
    ifeq ($(TARGET_ARCH_ABI),x86)
        OPENCV_3RDPARTY_COMPONENTS:=tbb libjpeg libpng libtiff libjasper IlmImf
        OPENCV_EXTRA_COMPONENTS:=c log m dl z
    endif
    ifeq ($(TARGET_ARCH_ABI),armeabi)
        OPENCV_3RDPARTY_COMPONENTS:= libjpeg libpng libtiff libjasper IlmImf
        OPENCV_EXTRA_COMPONENTS:=c log m dl z
    endif
    ifeq ($(TARGET_ARCH_ABI),mips)
        OPENCV_3RDPARTY_COMPONENTS:=tbb libjpeg libpng libtiff libjasper IlmImf
        OPENCV_EXTRA_COMPONENTS:=c log m dl z
    endif
endif


ifeq ($(OPENCV_CAMERA_MODULES),on) #引入相机库(只有动态库)
    ifeq ($(TARGET_ARCH_ABI),armeabi)
        OPENCV_CAMERA_MODULES:= native_camera_r2.2.0 native_camera_r2.3.3 native_camera_r3.0.1 native_camera_r4.0.0 native_camera_r4.0.3 native_camera_r4.1.1 native_camera_r4.2.0 native_camera_r4.3.0 native_camera_r4.4.0
    endif
    ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
        OPENCV_CAMERA_MODULES:= native_camera_r2.2.0 native_camera_r2.3.3 native_camera_r3.0.1 native_camera_r4.0.0 native_camera_r4.0.3 native_camera_r4.1.1 native_camera_r4.2.0 native_camera_r4.3.0 native_camera_r4.4.0
    endif
    ifeq ($(TARGET_ARCH_ABI),x86)
        OPENCV_CAMERA_MODULES:= native_camera_r2.3.3 native_camera_r3.0.1 native_camera_r4.0.3 native_camera_r4.1.1 native_camera_r4.2.0 native_camera_r4.3.0 native_camera_r4.4.0
    endif
    ifeq ($(TARGET_ARCH_ABI),mips)
        OPENCV_CAMERA_MODULES:= native_camera_r4.0.3 native_camera_r4.1.1 native_camera_r4.2.0 native_camera_r4.3.0 native_camera_r4.4.0
    endif
else
    OPENCV_CAMERA_MODULES:=
endif


ifeq ($(OPENCV_LIB_TYPE),SHARED)
    OPENCV_LIB_SUFFIX:=so
else
    OPENCV_LIB_SUFFIX:=a
    OPENCV_INSTALL_MODULES:=on
endif


define add_opencv_module #添加opencv库
    include $(CLEAR_VARS)
    LOCAL_MODULE:=opencv_$1
    LOCAL_SRC_FILES:=$(OPENCV_LIBS_DIR)/libopencv_$1.$(OPENCV_LIB_SUFFIX)
    include $(PREBUILT_$(OPENCV_LIB_TYPE)_LIBRARY)
endef


define add_cuda_module	#添加cuda库
    include $(CLEAR_VARS)
    LOCAL_MODULE:=$1
    LOCAL_SRC_FILES:=$(CUDA_TOOLKIT_DIR)/targets/armv7-linux-androideabi/lib/lib$1.so
    include $(PREBUILT_SHARED_LIBRARY)
endef


define add_opencv_3rdparty_component #添加3D库
    include $(CLEAR_VARS)
    LOCAL_MODULE:=$1
    LOCAL_SRC_FILES:=$(OPENCV_3RDPARTY_LIBS_DIR)/lib$1.a
    include $(PREBUILT_STATIC_LIBRARY)
endef


define add_opencv_camera_module #添加相机库
    include $(CLEAR_VARS)
    LOCAL_MODULE:=$1
    LOCAL_SRC_FILES:=$(OPENCV_LIBS_DIR)/lib$1.so
    include $(PREBUILT_SHARED_LIBRARY)
endef


ifeq ($(OPENCV_MK_$(OPENCV_TARGET_ARCH_ABI)_ALREADY_INCLUDED),)  #未包含则添加
    ifeq ($(OPENCV_INSTALL_MODULES),on)
        $(foreach module,$(OPENCV_LIBS),$(eval $(call add_opencv_module,$(module))))
        ifneq ($(OPENCV_DYNAMICUDA_MODULE),)
            ifeq ($(OPENCV_LIB_TYPE),SHARED)
              $(eval $(call add_opencv_module,$(OPENCV_DYNAMICUDA_MODULE)))
            endif
        endif
    endif


    ifeq ($(OPENCV_USE_GPU_MODULE),on)
        ifeq ($(INSTALL_CUDA_LIBRARIES),on)
            $(foreach module,$(CUDA_RUNTIME_LIBS),$(eval $(call add_cuda_module,$(module))))
        endif
    endif


    $(foreach module,$(OPENCV_3RDPARTY_COMPONENTS),$(eval $(call add_opencv_3rdparty_component,$(module))))
    $(foreach module,$(OPENCV_CAMERA_MODULES),$(eval $(call add_opencv_camera_module,$(module))))


    ifneq ($(OPENCV_BASEDIR),)
        OPENCV_LOCAL_C_INCLUDES += $(foreach mod, $(OPENCV_MODULES), $(OPENCV_BASEDIR)/modules/$(mod)/include)
        ifeq ($(OPENCV_USE_GPU_MODULE),on)
            OPENCV_LOCAL_C_INCLUDES += $(OPENCV_BASEDIR)/modules/gpu/include
        endif
    endif


    #turn off module installation to prevent their redefinition
    OPENCV_MK_$(OPENCV_TARGET_ARCH_ABI)_ALREADY_INCLUDED:=on
endif


ifeq ($(OPENCV_MK_$(OPENCV_TARGET_ARCH_ABI)_GPU_ALREADY_INCLUDED),) #未包含则添加
    ifeq ($(OPENCV_USE_GPU_MODULE),on)
        include $(CLEAR_VARS)
        LOCAL_MODULE:=opencv_gpu
        LOCAL_SRC_FILES:=$(OPENCV_LIBS_DIR)/libopencv_gpu.a
        include $(PREBUILT_STATIC_LIBRARY)
    endif
    OPENCV_MK_$(OPENCV_TARGET_ARCH_ABI)_GPU_ALREADY_INCLUDED:=on
endif


ifeq ($(OPENCV_LOCAL_CFLAGS),)	#标识
    OPENCV_LOCAL_CFLAGS := -fPIC -DANDROID -fsigned-char
endif


include $(CLEAR_VARS)


LOCAL_C_INCLUDES:=$(USER_LOCAL_C_INCLUDES)  #包含路径
LOCAL_CFLAGS:=$(USER_LOCAL_CFLAGS)	#标识
LOCAL_STATIC_LIBRARIES:=$(USER_LOCAL_STATIC_LIBRARIES)  #静态库
LOCAL_SHARED_LIBRARIES:=$(USER_LOCAL_SHARED_LIBRARIES)  #动态库
LOCAL_LDLIBS:=$(USER_LOCAL_LDLIBS)		#静态目录


LOCAL_C_INCLUDES += $(OPENCV_LOCAL_C_INCLUDES)  #追加包含路径
LOCAL_CFLAGS     += $(OPENCV_LOCAL_CFLAGS)	#追加标识


ifeq ($(OPENCV_USE_GPU_MODULE),on)	#使用gpu则添加cuda路径
    LOCAL_C_INCLUDES += $(CUDA_TOOLKIT_DIR)/include
endif


ifeq ($(OPENCV_INSTALL_MODULES),on) #
    LOCAL_$(OPENCV_LIB_TYPE)_LIBRARIES += $(foreach mod, $(OPENCV_LIBS), opencv_$(mod))
    ifeq ($(OPENCV_LIB_TYPE),SHARED)
        ifneq ($(OPENCV_DYNAMICUDA_MODULE),)
            LOCAL_$(OPENCV_LIB_TYPE)_LIBRARIES += $(OPENCV_DYNAMICUDA_MODULE)
        endif
    endif
else
    LOCAL_LDLIBS += -L$(call host-path,$(LOCAL_PATH)/$(OPENCV_LIBS_DIR)) $(foreach lib, $(OPENCV_LIBS), -lopencv_$(lib)) #追加目录
endif


ifeq ($(OPENCV_LIB_TYPE),STATIC) #静态 追加3D库
    LOCAL_STATIC_LIBRARIES += $(OPENCV_3RDPARTY_COMPONENTS)
endif


LOCAL_LDLIBS += $(foreach lib,$(OPENCV_EXTRA_COMPONENTS), -l$(lib)) #静态目录


ifeq ($(OPENCV_USE_GPU_MODULE),on)
    ifeq ($(INSTALL_CUDA_LIBRARIES),on)
        LOCAL_SHARED_LIBRARIES += $(foreach mod, $(CUDA_RUNTIME_LIBS), $(mod))
    else
        LOCAL_LDLIBS += -L$(CUDA_TOOLKIT_DIR)/targets/armv7-linux-androideabi/lib $(foreach lib, $(CUDA_RUNTIME_LIBS), -l$(lib))
    endif
    LOCAL_STATIC_LIBRARIES+=libopencv_gpu
endif


LOCAL_PATH:=$(USER_LOCAL_PATH) #恢复原路径



结束

OpenCV在Android上的应用,使图片、相机操作变得简单了,接下来掌握OpenCV直接使用技巧。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。