简体   繁体   中英

How to display a watermark on the camera preview

I'm making a motion detector app on top of this project. I would like to display a watermark image on the camera preview. I tried this method, but it didn't work for me. Please explain in code, how to show the watermark without having any issue for the motion detection part.

MotionDetection.java

public class MotionDetectionActivity extends SensorsActivity {

private static final String TAG = "MotionDetectionActivity";

private static SurfaceView preview = null;
private static SurfaceHolder previewHolder = null;
private static Camera camera = null;
private static boolean inPreview = false;
private static long mReferenceTime = 0;
private static IMotionDetection detector = null;

private static volatile AtomicBoolean processing = new AtomicBoolean(false);

/**
 * {@inheritDoc}
 */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    preview = (SurfaceView) findViewById(R.id.preview);
    previewHolder = preview.getHolder();
    previewHolder.addCallback(surfaceCallback);
    previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

    if (Preferences.USE_RGB) {
        detector = new RgbMotionDetection();
    } else if (Preferences.USE_LUMA) {
        detector = new LumaMotionDetection();
    } else {
        // Using State based (aggregate map)
        detector = new AggregateLumaMotionDetection();
    }
}

/**
 * {@inheritDoc}
 */
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
}

/**
 * {@inheritDoc}
 */
@Override
public void onPause() {
    super.onPause();

    camera.setPreviewCallback(null);
    if (inPreview) camera.stopPreview();
    inPreview = false;
    camera.release();
    camera = null;
}

/**
 * {@inheritDoc}
 */
@Override
public void onResume() {
    super.onResume();

    camera = Camera.open();
}

private PreviewCallback previewCallback = new PreviewCallback() {

    /**
     * {@inheritDoc}
     */
    @Override
    public void onPreviewFrame(byte[] data, Camera cam) {
        if (data == null) return;
        Camera.Size size = cam.getParameters().getPreviewSize();
        if (size == null) return;

        if (!GlobalData.isPhoneInMotion()) {
            DetectionThread thread = new DetectionThread(data, size.width, size.height);
            thread.start();
        }
    }
};

private SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {

    /**
     * {@inheritDoc}
     */
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            camera.setPreviewDisplay(previewHolder);
            camera.setPreviewCallback(previewCallback);
        } catch (Throwable t) {
            Log.e("PreviewDemo-surfaceCallback", "Exception in setPreviewDisplay()", t);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Camera.Parameters parameters = camera.getParameters();
        Camera.Size size = getBestPreviewSize(width, height, parameters);
        if (size != null) {
            parameters.setPreviewSize(size.width, size.height);
            Log.d(TAG, "Using width=" + size.width + " height=" + size.height);
        }
        camera.setParameters(parameters);
        camera.startPreview();
        inPreview = true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // Ignore
    }
};

private static Camera.Size getBestPreviewSize(int width, int height, Camera.Parameters parameters) {
    Camera.Size result = null;

    for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
        if (size.width <= width && size.height <= height) {
            if (result == null) {
                result = size;
            } else {
                int resultArea = result.width * result.height;
                int newArea = size.width * size.height;

                if (newArea > resultArea) result = size;
            }
        }
    }

    return result;
}

private static final class DetectionThread extends Thread {

    private byte[] data;
    private int width;
    private int height;

    public DetectionThread(byte[] data, int width, int height) {
        this.data = data;
        this.width = width;
        this.height = height;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void run() {
        if (!processing.compareAndSet(false, true)) return;

        // Log.d(TAG, "BEGIN PROCESSING...");
        try {
            // Previous frame
            int[] pre = null;
            if (Preferences.SAVE_PREVIOUS) pre = detector.getPrevious();

            // Current frame (with changes)
            // long bConversion = System.currentTimeMillis();
            int[] img = null;
            if (Preferences.USE_RGB) {
                img = ImageProcessing.decodeYUV420SPtoRGB(data, width, height);
            } else {
                img = ImageProcessing.decodeYUV420SPtoLuma(data, width, height);
            }
            // long aConversion = System.currentTimeMillis();
            // Log.d(TAG, "Converstion="+(aConversion-bConversion));

            // Current frame (without changes)
            int[] org = null;
            if (Preferences.SAVE_ORIGINAL && img != null) org = img.clone();

            if (img != null && detector.detect(img, width, height)) {
                // The delay is necessary to avoid taking a picture while in
                // the
                // middle of taking another. This problem can causes some
                // phones
                // to reboot.
                long now = System.currentTimeMillis();
                if (now > (mReferenceTime + Preferences.PICTURE_DELAY)) {
                    mReferenceTime = now;

                    Bitmap previous = null;
                    if (Preferences.SAVE_PREVIOUS && pre != null) {
                        if (Preferences.USE_RGB) previous = ImageProcessing.rgbToBitmap(pre, width, height);
                        else previous = ImageProcessing.lumaToGreyscale(pre, width, height);
                    }

                    Bitmap original = null;
                    if (Preferences.SAVE_ORIGINAL && org != null) {
                        if (Preferences.USE_RGB) original = ImageProcessing.rgbToBitmap(org, width, height);
                        else original = ImageProcessing.lumaToGreyscale(org, width, height);
                    }

                    Bitmap bitmap = null;
                    if (Preferences.SAVE_CHANGES) {
                        if (Preferences.USE_RGB) bitmap = ImageProcessing.rgbToBitmap(img, width, height);
                        else bitmap = ImageProcessing.lumaToGreyscale(img, width, height);
                    }

                    Log.i(TAG, "Saving.. previous=" + previous + " original=" + original + " bitmap=" + bitmap);
                    Looper.prepare();
                    new SavePhotoTask().execute(previous, original, bitmap);
                } else {
                    Log.i(TAG, "Not taking picture because not enough time has passed since the creation of the Surface");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            processing.set(false);
        }
        // Log.d(TAG, "END PROCESSING...");

        processing.set(false);
    }
};

private static final class SavePhotoTask extends AsyncTask<Bitmap, Integer, Integer> {

    /**
     * {@inheritDoc}
     */
    @Override
    protected Integer doInBackground(Bitmap... data) {
        for (int i = 0; i < data.length; i++) {
            Bitmap bitmap = data[i];
            String name = String.valueOf(System.currentTimeMillis());
            if (bitmap != null) save(name, bitmap);
        }
        return 1;
    }

    private void save(String name, Bitmap bitmap) {
        File photo = new File(Environment.getExternalStorageDirectory(), name + ".jpg");
        if (photo.exists()) photo.delete();

        try {
            FileOutputStream fos = new FileOutputStream(photo.getPath());
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.close();
        } catch (java.io.IOException e) {
            Log.e("PictureDemo", "Exception in photoCallback", e);
        }
    }
}
}

main.xml

<?xml version="1.0" encoding="utf-8"?>
<SurfaceView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/preview"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</SurfaceView>

A workaround option is to overlay the Activity XML file with another XML file which contains the watermark image. To do so:

  1. Create a new Layout file inside the layout folder. For eg: overlay.xml
  2. Insert an ImageView inside it, something like:

     <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"> <ImageView android:id="@+id/imageView1" android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/android" /> </RelativeLayout> 
  3. Then inside the Activity java file, ie MotionDetector.java file , create a new method addView() :

      private void addView() { controlInflater = LayoutInflater.from(getBaseContext()); View viewControl = controlInflater.inflate(R.layout.overlay, null); LayoutParams layoutParamsControl = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); this.addContentView(viewControl, layoutParamsControl); } 

4) Finally invoke the addView() method from onCreate() to add the image:

super.onCreate(savedInstanceState);
setContentView(R.layout.main);

preview = (SurfaceView) findViewById(R.id.preview);
previewHolder = preview.getHolder();
previewHolder.addCallback(surfaceCallback);
previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
addView();

Then end result would be an image overlay above the SurfaceView . Subject to the quality of the watermark image, the rendered quality of the watermark shall seem original. Hope it helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM