<|start_filename|>app/src/main/java/com/example/root/opencvobjecttracking/MainActivity.java<|end_filename|>
package com.example.root.opencvobjecttracking;

import android.Manifest;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.camera2.params.BlackLevelPattern;
import android.support.annotation.NonNull;
import android.support.annotation.RawRes;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.view.Display;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;

import com.example.root.cameramodule.CameraApi;
import com.example.root.cameramodule.ICameraApiCallback;

import org.opencv.android.InstallCallbackInterface;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Rect2d;
import org.opencv.tracking.Tracker;
import org.opencv.tracking.TrackerBoosting;
import org.opencv.tracking.TrackerCSRT;
import org.opencv.tracking.TrackerKCF;
import org.opencv.tracking.TrackerMIL;
import org.opencv.tracking.TrackerMedianFlow;
import org.opencv.tracking.TrackerTLD;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, ICameraApiCallback
        , View.OnClickListener, ResizeRectangleView.CropVisionFinishCallback {
    private static final String TAG = "MainActivity";
    private final int PERMISSION_CAMERA_REQUEST_CODE = 0x10;
    private boolean isCheckPermissionOk = false;
    private boolean isLoadSuccess = false;
    private volatile boolean isStartTracking = false;
    private volatile boolean isSelectArea = false;
    private boolean isInit = false;
    /**
     * 隔一帧来取一帧数据进行追踪，可以
     */
    private boolean isSkip = true;

    private SurfaceView mSVContent;
    private ResizeRectangleView mResizeRectView;
    private Button mResetView;
    private Button mChooseTrackerView;
    private ViewGroup.LayoutParams layoutParams;
    private SurfaceHolder mSurfaceHolder;
    private float ratio;
    private int mSurfaceViewWidth;
    private int mSurfaceViewHeight;
    public static int previewWidth = 1280;
    public static int previewHeight = 720;

    private int mCurrentCameraIndex = CameraApi.CAMERA_INDEX_BACK;

    private CameraRawData mCameraRawData;
    private final int BUFFER_SIZE = 5;
    private BlockingQueue<CameraRawData> mFreeQueue = new LinkedBlockingQueue<>(BUFFER_SIZE);
    private BlockingQueue<CameraRawData> mFrameDataQueue = new LinkedBlockingQueue<>(BUFFER_SIZE);

    private Mat mSrcMat;
    /**
     * for front camera need flip data
     */
    private Mat mDesMat = null;
    private Tracker mTracker;
    private Rect2d rect2d;

    private Thread mTrackingThread = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSVContent = findViewById(R.id.sv_content);
        mSVContent.getHolder().addCallback(this);
        mResizeRectView = findViewById(R.id.resize_rect);
        mResetView = findViewById(R.id.reset_track);
        mChooseTrackerView = findViewById(R.id.choose_tracker);
        mResetView.setOnClickListener(this);
        mChooseTrackerView.setOnClickListener(this);
        mResizeRectView.setmUpCallback(this);

        int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
        if (permission != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_CAMERA_REQUEST_CODE);
        } else {
            isCheckPermissionOk = true;
        }

        Display display = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getRealSize(size);
        mSurfaceViewWidth = size.x;
        mSurfaceViewHeight = size.y;
        Log.e(TAG, "onCreate: " + size.x + "--" + size.y);
    }


    @Override
    protected void onResume() {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.e(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_4_0, this, mLoaderCallback);
        } else {
            Log.e(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        reset();
        mSrcMat.release();
        mSrcMat = null;
        if (mDesMat != null) {
            mDesMat.release();
            mDesMat = null;
        }
    }

    private void init() {
        mSrcMat = new Mat(previewHeight, previewWidth, CvType.CV_8UC1);
        if (mCurrentCameraIndex == CameraApi.CAMERA_INDEX_FRONT) {
            mDesMat = new Mat(previewHeight, previewWidth, CvType.CV_8UC1);
        }
        mTracker = TrackerKCF.create();
        rect2d = new Rect2d();

        if (mFreeQueue.isEmpty()) {
            for (int i = 0; i < BUFFER_SIZE; i++) {
                CameraRawData rawData = new CameraRawData();
                mFreeQueue.offer(rawData);
            }
        }
    }

    private LoaderCallbackInterface mLoaderCallback = new LoaderCallbackInterface() {
        @Override
        public void onManagerConnected(int status) {
            if (status == LoaderCallbackInterface.SUCCESS) {
                isLoadSuccess = true;
                init();
            }
        }

        @Override
        public void onPackageInstall(int operation, InstallCallbackInterface callback) {

        }
    };


    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        if (isCheckPermissionOk) {
            CameraApi.getInstance().setCameraId(mCurrentCameraIndex);
            CameraApi.getInstance().initCamera(this, this);
            CameraApi.getInstance().setPreviewSize(new Size(previewWidth, previewHeight));
            CameraApi.getInstance().setFps(30).configCamera();
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mSurfaceHolder = holder;
        ratio = (float) (previewWidth) / (float) (previewHeight);
        layoutParams = mSVContent.getLayoutParams();
        if (mSurfaceViewHeight > mSurfaceViewWidth) {
            //竖屏
            mSurfaceViewHeight = (int) (mSurfaceViewWidth * ratio);
        } else {
            //横屏
            mSurfaceViewWidth = (int) (mSurfaceViewHeight * ratio);
        }

        Log.e(TAG, "surfaceChanged:mSurfaceViewWidth= " + mSurfaceViewWidth);
        Log.e(TAG, "surfaceChanged:mSurfaceViewHeight= " + mSurfaceViewHeight);
        layoutParams.width = mSurfaceViewWidth;
        layoutParams.height = mSurfaceViewHeight;
        mSVContent.setLayoutParams(layoutParams);
        if (isCheckPermissionOk) {
            CameraApi.getInstance().startPreview(holder);
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.reset_track:
                reset();
                mResizeRectView.onClearCanvas();
                break;
            case R.id.choose_tracker:
                showDialog(Constant.DIALOG_ID_SHOW_TRACKER_LIST);
                break;
            default:
                break;
        }
    }

    private void reset() {
        isStartTracking = false;
        isSelectArea = false;
        if (mTrackingThread != null) {
            try {
                mTrackingThread.join();
                mTrackingThread = null;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        mTracker = null;
        isInit = false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mResizeRectView.onTouchEvent(event);
        return super.onTouchEvent(event);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PERMISSION_CAMERA_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                isCheckPermissionOk = true;
                onStartPreview();
            }
        }
    }

    private void onStartPreview() {
        CameraApi.getInstance().setCameraId(mCurrentCameraIndex);
        CameraApi.getInstance().initCamera(this, this);
        CameraApi.getInstance().setPreviewSize(new Size(previewWidth, previewHeight));
        CameraApi.getInstance().setFps(30).configCamera();
        CameraApi.getInstance().startPreview(mSurfaceHolder);
    }

    @Override
    public void onPreviewFrameCallback(byte[] data, Camera camera) {
        camera.addCallbackBuffer(data);
        if (isStartTracking) {
            CameraRawData rawData = mFreeQueue.poll();
            if (rawData != null) {
                rawData.setRawData(data);
                rawData.setTimestamp(System.currentTimeMillis());
                mFrameDataQueue.offer(rawData);
            }
        }
    }

    @Override
    public void onNotSupportErrorTip(String message) {

    }

    @Override
    public void onCameraInit(Camera camera) {

    }

    @Override
    public void onCropVisionAreaFinish(Rect rect) {
        isStartTracking = true;
        isSelectArea = true;
        rect2d.x = rect.left;
        rect2d.y = rect.top;
        rect2d.width = Math.abs(rect.right - rect.left);
        rect2d.height = Math.abs(rect.bottom - rect.top);

        if (mTrackingThread == null) {
            mTrackingThread = new ObjectTrackingThread("obj-tracking-thread");
            mTrackingThread.start();
        }
        if (mTracker == null) {
            mTracker = TrackerKCF.create();
        }
    }


    /**
     * tracking thread
     */
    boolean isUpdate;

    class ObjectTrackingThread extends Thread {
        public ObjectTrackingThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            super.run();
            while (isStartTracking && isLoadSuccess) {
                try {
                    mCameraRawData = mFrameDataQueue.poll(20, TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (mCameraRawData == null) {
                    continue;
                }
                isSkip = !isSkip;
                if (isSkip) {
                    mFreeQueue.offer(mCameraRawData);
                    continue;
                }
                byte[] data = mCameraRawData.getRawData();
                mSrcMat.put(0, 0, data);
                if (mDesMat != null) {
                    Core.flip(mSrcMat, mDesMat, 1);
                    if (!isInit) {
                        isInit = mTracker.init(mDesMat, rect2d);
                    }
                    isUpdate = mTracker.update(mDesMat, rect2d);
                } else {
                    if (!isInit) {
                        isInit = mTracker.init(mSrcMat, rect2d);
                    }
                    isUpdate = mTracker.update(mSrcMat, rect2d);
                }
                mResizeRectView.setTrackResult(isUpdate);
                if (isUpdate) {

                    mResizeRectView.onShowTracking(rect2d);
                }
//                Log.e(TAG, "run: 2222---isInit = " + isInit + "---isUpdate = " + isUpdate);
//                Log.e(TAG, "run: 333---" + rect2d.x + "--" + rect2d.y);
                mFreeQueue.offer(mCameraRawData);
            }
        }
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
            case Constant.DIALOG_ID_SHOW_TRACKER_LIST:
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle("选择Tracker").setItems(new CharSequence[]{"KCFTracker", "TLDTracker"
                                , "MedianFlowTracker", "MILTracker", "CSRTTracker"}
                        , mDialogItemListener);

                return builder.create();
        }
        return super.onCreateDialog(id);
    }


    private DialogInterface.OnClickListener mDialogItemListener = new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialogInterface, int i) {
            switch (i) {
                case 0:
                    //KCFTracker
                    reset();
                    mResizeRectView.onClearCanvas();
                    mTracker = TrackerKCF.create();
                    Toast.makeText(MainActivity.this, "selected TrackerKCF", Toast.LENGTH_SHORT).show();
                    break;
                case 1:
                    reset();
                    mResizeRectView.onClearCanvas();
                    mTracker = TrackerTLD.create();
                    Toast.makeText(MainActivity.this, "selected TrackerTLD", Toast.LENGTH_SHORT).show();
                    break;
                case 2:
                    reset();
                    mResizeRectView.onClearCanvas();
                    mTracker = TrackerMedianFlow.create();
                    Toast.makeText(MainActivity.this, "selected TrackerMedianFlow", Toast.LENGTH_SHORT).show();
                    break;
                case 3:
                    reset();
                    mResizeRectView.onClearCanvas();
                    mTracker = TrackerMIL.create();
                    Toast.makeText(MainActivity.this, "selected TrackerMIL", Toast.LENGTH_SHORT).show();
                    break;
                case 4:
                    reset();
                    mResizeRectView.onClearCanvas();
                    mTracker = TrackerCSRT.create();
                    Toast.makeText(MainActivity.this, "selected TrackerCSRT", Toast.LENGTH_SHORT).show();
                    break;
//                case 5:
//                    reset();
//                    mResizeRectView.onClearCanvas();
//                    mTracker = TrackerBoosting.create();
//                    Toast.makeText(MainActivity.this,"selected TrackerBoosting",Toast.LENGTH_SHORT).show();
//                    break;
            }

        }
    };
}


<|start_filename|>app/src/main/java/com/example/root/opencvobjecttracking/Constant.java<|end_filename|>
package com.example.root.opencvobjecttracking;

public class Constant {
    public static final int DIALOG_ID_SHOW_TRACKER_LIST = 0;
}


<|start_filename|>cameraModule/src/main/java/com/example/root/cameramodule/CameraApi.java<|end_filename|>
package com.example.root.cameramodule;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.WindowManager;

import java.io.IOException;
import java.util.List;

public class CameraApi implements Camera.PreviewCallback {
    private static final String TAG = "CameraApi";
    private static volatile CameraApi CAMERA_INSTANCE = null;
    public static final int CAMERA_INDEX_BACK = 0;
    public static final int CAMERA_INDEX_FRONT = 1;
    private final int DEFAULT_FPS = 30;
    private final int DEFAULT_PREVIEW_WIDTH = 1920;
    private final int DEFAULT_PREVIEW_HEIGHT = 1080;
    /**
     * default buffer number is 5
     */
    private final int BUFFER_COUNT = 5;

    private int mCameraId = CAMERA_INDEX_BACK;
    private int mPreviewWidth = DEFAULT_PREVIEW_WIDTH;
    private int mPreviewHeight = DEFAULT_PREVIEW_HEIGHT;
    private int fps = DEFAULT_FPS;

    private Context context;
    private ICameraApiCallback cameraApiCallback;

    private Camera mCamera = null;
    private Camera.Parameters mCameraParameters;

    private CameraApi() {
    }

    public static CameraApi getInstance() {
        if (CAMERA_INSTANCE == null) {
            synchronized (CameraApi.class) {
                if (CAMERA_INSTANCE == null) {
                    CAMERA_INSTANCE = new CameraApi();
                }
            }
        }
        return CAMERA_INSTANCE;
    }

    /**
     * set fps
     *
     * @param fps
     * @return
     */
    public CameraApi setFps(int fps) {
        this.fps = fps;
        return CAMERA_INSTANCE;
    }

    /**
     * set PreviewSize
     *
     * @param size
     * @return
     */
    public CameraApi setPreviewSize(Size size) {
        mPreviewWidth = size.getWidth();
        mPreviewHeight = size.getHeight();
        return CAMERA_INSTANCE;
    }

    /**
     * set camera id
     *
     * @param id
     * @return
     */
    public CameraApi setCameraId(int id) {
        this.mCameraId = id;
        return CAMERA_INSTANCE;
    }


    public synchronized void initCamera(Context context, ICameraApiCallback cameraApiCallback) {
        this.context = context;
        this.cameraApiCallback = cameraApiCallback;
        if (mCamera == null) {
            if (context.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                if (this.cameraApiCallback != null) {
                    this.cameraApiCallback.onNotSupportErrorTip("no permission");
                    return;
                }
            }
            int cameraNumber = Camera.getNumberOfCameras();
            if (cameraNumber == 0) {
                Log.e(TAG, "initCamera: This Devices is no Camera");
                return;
            }
            mCamera = Camera.open(mCameraId);
            cameraApiCallback.onCameraInit(mCamera);
        }
    }


    public synchronized CameraApi configCamera() {
        if (mCamera == null) {
            if (cameraApiCallback != null)
                cameraApiCallback.onNotSupportErrorTip(context.getString(R.string.init_camera_error));
            return this;
        }
        mCameraParameters = mCamera.getParameters();
        List<Integer> supportFormat = mCameraParameters.getSupportedPreviewFormats();
        for (Integer f : supportFormat
                ) {
            Log.e(TAG, "configCamera: f = " + f + "--");
        }
        List<Camera.Size> supportedPreviewSizes = mCameraParameters.getSupportedPreviewSizes();
        if (!isPreviewSizeSupport(supportedPreviewSizes)) {
            mPreviewWidth = DEFAULT_PREVIEW_WIDTH;
            mPreviewHeight = DEFAULT_PREVIEW_HEIGHT;
        }
        mCameraParameters.setPreviewFormat(ImageFormat.NV21);
        mCameraParameters.setPreviewSize(mPreviewWidth, mPreviewHeight);
        setCameraFps(mCameraParameters, fps);
        mCamera.setParameters(mCameraParameters);
        setupOrientaition(context);
        for (int i = 0; i < BUFFER_COUNT; i++) {
            mCamera.addCallbackBuffer(new byte[mPreviewHeight * mPreviewWidth * 3 / 2]);
        }
        return this;
    }

    private boolean isPreviewSizeSupport(List<Camera.Size> sizes) {
        boolean isSupport = false;
        for (Camera.Size s : sizes
                ) {
            if (s.width == mPreviewWidth && s.height == mPreviewHeight) {
                isSupport = true;
            }
            Log.e(TAG, "isPreviewSizeSupport: supportPreviewSize = " + s.width + "--" + s.height + ";");
        }
        return isSupport;
    }

    private void setupOrientaition(Context context) {
        final int rotation = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
        int degrees = 0;
        int displayRotation;
        Log.e(TAG, "setupOrientaition: rotation=" + rotation);
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break; // Natural orientation
            case Surface.ROTATION_90:
                degrees = 90;
                break; // Landscape left
            case Surface.ROTATION_180:
                degrees = 180;
                break;// Upside down
            case Surface.ROTATION_270:
                degrees = 270;
                break;// Landscape right
            default:
                break;
        }

        android.hardware.Camera.CameraInfo cameraInfo = new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(0, cameraInfo);

        //根据前置与后置摄像头的不同，设置预览方向，否则会发生预览图像倒过来的情况。
        if (cameraInfo.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT) {
            displayRotation = (cameraInfo.orientation + degrees) % 360;
            displayRotation = (360 - displayRotation) % 360; // compensate
        } else {
            displayRotation = (cameraInfo.orientation - degrees + 360) % 360;
        }
        this.mCamera.setDisplayOrientation(displayRotation);
    }

    public synchronized void startPreview(SurfaceHolder holder) {
        mCamera.setPreviewCallbackWithBuffer(this);
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public synchronized void stopCamera() {
        if (mCamera != null) {
            mCamera.release();
            mCamera = null;
        }
    }

    public void addCallbackBuffer(byte[] bytes){
        mCamera.addCallbackBuffer(bytes);
    }

    /**
     * preview frame data callback
     *
     * @param data
     * @param camera
     */
    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        if (cameraApiCallback != null) {
            cameraApiCallback.onPreviewFrameCallback(data, camera);
        }
    }

    private void setCameraFps(Camera.Parameters p, int mFramerate) {
        List<int[]> range = p.getSupportedPreviewFpsRange();
        int i;
        int size = range.size();
        int[] r = range.get(0);
        int fps = mFramerate * 1000;
        boolean found = false;

        /*find fixed fps range*/
        for (i = 0; i < size; i++) {
            r = range.get(i);
            Log.d(TAG, "camera PFS[" + r[0] + " " + r[1] + "]");
            if (fps == r[0] && fps == r[1]) {
                Log.d(TAG, "Found fixed fps range, use it");
                found = true;
                break;
            }
        }

        /*find variable fps range*/
        for (i = 0; i < size && !found; i++) {
            r = range.get(i);
            Log.d(TAG, "camera PFS[" + r[0] + " " + r[1] + "]");
            if (fps >= r[0] && fps <= r[1]) {
                Log.d(TAG, "Found variable fps range, use it");
                found = true;
                break;
            }
        }

        if (!found) {
            Log.d(TAG, "Use first FPS range");
            r = range.get(0);
        }

        Log.d(TAG, "Set camera PFS[" + r[0] + " " + r[1] + "]");
        p.setPreviewFpsRange(r[0], r[1]);
    }
}
