com.trandi.opentld.TLDView.java Source code

Java tutorial

Introduction

Here is the source code for com.trandi.opentld.TLDView.java

Source

/**
 * Copyright 2013 Dan Oprescu
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 *     
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.trandi.opentld;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;

import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.JavaCameraView;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;

import com.trandi.opentld.tld.Tld;
import com.trandi.opentld.tld.Tld.ProcessFrameStruct;
import com.trandi.opentld.tld.Util;

public class TLDView extends JavaCameraView implements CameraBridgeViewBase.CvCameraViewListener {
    final private SurfaceHolder _holder;
    private int _canvasImgYOffset;
    private int _canvasImgXOffset;

    private Mat _currentGray = new Mat();
    private Mat _lastGray = new Mat();
    private Tld _tld = null;
    private Rect _trackedBox = null;
    private ProcessFrameStruct _processFrameStruct = null;
    private Properties _tldProperties;

    private static final Size WORKING_FRAME_SIZE = new Size(144, 80);
    private Mat _workingFrame = new Mat();
    private String _errMessage;

    public TLDView(Context context, AttributeSet attrs) {
        super(context, attrs);
        _holder = getHolder();

        // Init the PROPERTIES
        InputStream propsIS = null;
        try {
            propsIS = context.getResources().openRawResource(R.raw.parameters);
            _tldProperties = new Properties();
            _tldProperties.load(propsIS);
        } catch (IOException e) {
            Log.e(Util.TAG, "Can't load properties", e);
        } finally {
            if (propsIS != null) {
                try {
                    propsIS.close();
                } catch (IOException e) {
                    Log.e(Util.TAG, "Can't close props", e);
                }
            }
        }

        // listens to its own events
        setCvCameraViewListener(this);

        // DEBUG
        //_trackedBox = new BoundingBox(165,93,51,54, 0, 0);

        // LISTEN for touches of the screen, to define the BOX to be tracked
        final AtomicReference<Point> trackedBox1stCorner = new AtomicReference<Point>();
        final Paint rectPaint = new Paint();
        rectPaint.setColor(Color.rgb(0, 255, 0));
        rectPaint.setStrokeWidth(5);
        rectPaint.setStyle(Style.STROKE);

        setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // re-init
                _errMessage = null;
                _tld = null;

                final Point corner = new Point(event.getX() - _canvasImgXOffset, event.getY() - _canvasImgYOffset);
                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    trackedBox1stCorner.set(corner);
                    Log.i(Util.TAG, "1st corner: " + corner);
                    break;
                case MotionEvent.ACTION_UP:
                    _trackedBox = new Rect(trackedBox1stCorner.get(), corner);
                    Log.i(Util.TAG, "Tracked box DEFINED: " + _trackedBox);
                    break;
                case MotionEvent.ACTION_MOVE:
                    final android.graphics.Rect rect = new android.graphics.Rect(
                            (int) trackedBox1stCorner.get().x + _canvasImgXOffset,
                            (int) trackedBox1stCorner.get().y + _canvasImgYOffset,
                            (int) corner.x + _canvasImgXOffset, (int) corner.y + _canvasImgYOffset);
                    final Canvas canvas = _holder.lockCanvas(rect);
                    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); // remove old rectangle
                    canvas.drawRect(rect, rectPaint);
                    _holder.unlockCanvasAndPost(canvas);
                    break;
                }

                return true;
            }
        });
    }

    @Override
    public Mat onCameraFrame(Mat originalFrame) {
        try {
            // Image is too big and this requires too much CPU for a phone, so scale everything down...
            Imgproc.resize(originalFrame, _workingFrame, WORKING_FRAME_SIZE);
            final Size workingRatio = new Size(originalFrame.width() / WORKING_FRAME_SIZE.width,
                    originalFrame.height() / WORKING_FRAME_SIZE.height);
            // usefull to see what we're actually working with...
            _workingFrame.copyTo(originalFrame.submat(originalFrame.rows() - _workingFrame.rows(),
                    originalFrame.rows(), 0, _workingFrame.cols()));

            if (_trackedBox != null) {
                if (_tld == null) { // run the 1st time only
                    Imgproc.cvtColor(_workingFrame, _lastGray, Imgproc.COLOR_RGB2GRAY);
                    _tld = new Tld(_tldProperties);
                    final Rect scaledDownTrackedBox = scaleDown(_trackedBox, workingRatio);
                    Log.i(Util.TAG, "Working Ration: " + workingRatio + " / Tracking Box: " + _trackedBox
                            + " / Scaled down to: " + scaledDownTrackedBox);
                    try {
                        _tld.init(_lastGray, scaledDownTrackedBox);
                    } catch (Exception eInit) {
                        // start from scratch, you have to select an init box again !
                        _trackedBox = null;
                        _tld = null;
                        throw eInit; // re-throw it as it will be dealt with later
                    }
                } else {
                    Imgproc.cvtColor(_workingFrame, _currentGray, Imgproc.COLOR_RGB2GRAY);

                    _processFrameStruct = _tld.processFrame(_lastGray, _currentGray);
                    drawPoints(originalFrame, _processFrameStruct.lastPoints, workingRatio, new Scalar(255, 0, 0));
                    drawPoints(originalFrame, _processFrameStruct.currentPoints, workingRatio,
                            new Scalar(0, 255, 0));
                    drawBox(originalFrame, scaleUp(_processFrameStruct.currentBBox, workingRatio),
                            new Scalar(0, 0, 255));

                    _currentGray.copyTo(_lastGray);

                    // overlay the current positive examples on the real image(needs converting at the same time !)
                    //copyTo(_tld.getPPatterns(), originalFrame);
                }
            }
        } catch (Exception e) {
            _errMessage = e.getClass().getSimpleName() + " / " + e.getMessage();
            Log.e(Util.TAG, "TLDView PROBLEM", e);
        }

        if (_errMessage != null) {
            Imgproc.putText(originalFrame, _errMessage, new Point(0, 300), Core.FONT_HERSHEY_PLAIN, 1.3d,
                    new Scalar(255, 0, 0), 2);
        }

        return originalFrame;
    }

    private static void copyTo(List<Mat> patterns, Mat dest) {
        if (patterns == null || patterns.isEmpty() || dest == null)
            return;

        final int patternRows = patterns.get(0).rows();
        final int patternCols = patterns.get(0).cols();
        final int vertCount = dest.rows() / patternRows;
        final int horizCount = patterns.size() / vertCount + 1;

        int patchIdx = 0;
        for (int col = dest.cols() - horizCount * patternCols - 1; col < dest.cols()
                && patchIdx < patterns.size(); col += patternCols) {
            for (int row = 0; row < dest.rows() && patchIdx < patterns.size(); row += patternRows) {
                Imgproc.cvtColor(patterns.get(patchIdx),
                        dest.submat(row, row + patternRows, col, col + patternCols), Imgproc.COLOR_GRAY2RGBA);
                patchIdx++;
            }
        }
    }

    @Override
    public void onCameraViewStarted(int width, int height) {
        _canvasImgXOffset = (getWidth() - width) / 2;
        _canvasImgYOffset = (getHeight() - height) / 2;
    }

    @Override
    public void onCameraViewStopped() {
        // TODO Auto-generated method stub
    }

    private static void drawPoints(Mat image, final Point[] points, final Size scale, final Scalar colour) {
        if (points != null) {
            for (Point point : points) {
                Imgproc.circle(image, scaleUp(point, scale), 2, colour);
            }
        }
    }

    private static void drawBox(Mat image, final Rect box, final Scalar colour) {
        if (box != null) {
            Imgproc.rectangle(image, box.tl(), box.br(), colour);
        }
    }

    /* SCALING */

    private static Point scaleUp(Point point, Size scale) {
        if (point == null || scale == null)
            return null;
        return new Point(point.x * scale.width, point.y * scale.height);
    }

    private static Point scaleDown(Point point, Size scale) {
        if (point == null || scale == null)
            return null;
        return new Point(point.x / scale.width, point.y / scale.height);
    }

    private static Rect scaleUp(Rect rect, Size scale) {
        if (rect == null || scale == null)
            return null;
        return new Rect(scaleUp(rect.tl(), scale), scaleUp(rect.br(), scale));
    }

    private static Rect scaleDown(Rect rect, Size scale) {
        if (rect == null || scale == null)
            return null;
        return new Rect(scaleDown(rect.tl(), scale), scaleDown(rect.br(), scale));
    }
}