androidgoogle-visionvision-api

Take picture with drawable/paint on face using vision api


What I am trying?

I am trying to take picture with drawable/paint on face but, i am not able to get both on same picture.

enter image description here

What I have tried?

I have tried using CameraSource.takePicture but i am just getting face without any drawable/paint on it.

mCameraSource.takePicture(shutterCallback, new CameraSource.PictureCallback() {
            @Override
            public void onPictureTaken(byte[] bytes) {
                try {
                    String mainpath = getExternalStorageDirectory() + separator + "TestXyz" + separator + "images" + separator;
                    File basePath = new File(mainpath);
                    if (!basePath.exists())
                        Log.d("CAPTURE_BASE_PATH", basePath.mkdirs() ? "Success": "Failed");
                    String path = mainpath + "photo_" + getPhotoTime() + ".jpg";
                    File captureFile = new File(path);
                    captureFile.createNewFile();
                    if (!captureFile.exists())
                        Log.d("CAPTURE_FILE_PATH", captureFile.createNewFile() ? "Success": "Failed");
                    FileOutputStream stream = new FileOutputStream(captureFile);
                    stream.write(bytes);
                    stream.flush();
                    stream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

I also tried using :

mPreview.setDrawingCacheEnabled(true);
        Bitmap drawingCache = mPreview.getDrawingCache();
        try {
            String mainpath = getExternalStorageDirectory() + separator + "TestXyz" + separator + "images" + separator;
            File basePath = new File(mainpath);
            if (!basePath.exists())
                Log.d("CAPTURE_BASE_PATH", basePath.mkdirs() ? "Success": "Failed");
            String path = mainpath + "photo_" + getPhotoTime() + ".jpg";
            File captureFile = new File(path);
            captureFile.createNewFile();
            if (!captureFile.exists())
                Log.d("CAPTURE_FILE_PATH", captureFile.createNewFile() ? "Success": "Failed");
            FileOutputStream stream = new FileOutputStream(captureFile);
            drawingCache.compress(Bitmap.CompressFormat.PNG, 100, stream);
            stream.flush();
            stream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

in this case i am only getting what i draw on face. Here, mPreview is the CameraSourcePreview.

Just added capture button and added above code in this google example.


Solution

  • I am able to capture image with drawable/paint on it by below solution :

    private void captureImage() {
            mPreview.setDrawingCacheEnabled(true);
            Bitmap drawingCache = mPreview.getDrawingCache();
    
            mCameraSource.takePicture(shutterCallback, new CameraSource.PictureCallback() {
                @Override
                public void onPictureTaken(byte[] bytes) {
                    int orientation = Exif.getOrientation(bytes);
                    Bitmap temp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                    Bitmap picture = rotateImage(temp,orientation);
                    Bitmap overlay = Bitmap.createBitmap(mGraphicOverlay.getWidth(),mGraphicOverlay.getHeight(),picture.getConfig());
                    Canvas canvas = new Canvas(overlay);
    
                    Matrix matrix = new Matrix();
    
                    matrix.setScale((float)overlay.getWidth()/(float)picture.getWidth(),(float)overlay.getHeight()/(float)picture.getHeight());
    
                    // mirror by inverting scale and translating
                    matrix.preScale(-1, 1);
                    matrix.postTranslate(canvas.getWidth(), 0);
    
                    Paint paint = new Paint();
                    canvas.drawBitmap(picture,matrix,paint);
                    canvas.drawBitmap(drawingCache,0,0,paint);
    
                    try {
                        String mainpath = getExternalStorageDirectory() + separator + "MaskIt" + separator + "images" + separator;
                        File basePath = new File(mainpath);
                        if (!basePath.exists())
                            Log.d("CAPTURE_BASE_PATH", basePath.mkdirs() ? "Success": "Failed");
                        String path = mainpath + "photo_" + getPhotoTime() + ".jpg";
                        File captureFile = new File(path);
                        captureFile.createNewFile();
                        if (!captureFile.exists())
                            Log.d("CAPTURE_FILE_PATH", captureFile.createNewFile() ? "Success": "Failed");
                        FileOutputStream stream = new FileOutputStream(captureFile);
                        overlay.compress(Bitmap.CompressFormat.PNG, 100, stream);
                        stream.flush();
                        stream.close();
                        picture.recycle();
                        drawingCache.recycle();
                        mPreview.setDrawingCacheEnabled(false);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    

    Sometimes orientation issue also occurs on some devices. For that i used Exif class and rotateImage() function.

    Exif Class (reference from here) :

    public class Exif {
        private static final String TAG = "CameraExif";
    
        // Returns the degrees in clockwise. Values are 0, 90, 180, or 270.
        public static int getOrientation(byte[] jpeg) {
            if (jpeg == null) {
                return 0;
            }
    
            int offset = 0;
            int length = 0;
    
            // ISO/IEC 10918-1:1993(E)
            while (offset + 3 < jpeg.length && (jpeg[offset++] & 0xFF) == 0xFF) {
                int marker = jpeg[offset] & 0xFF;
    
                // Check if the marker is a padding.
                if (marker == 0xFF) {
                    continue;
                }
                offset++;
    
                // Check if the marker is SOI or TEM.
                if (marker == 0xD8 || marker == 0x01) {
                    continue;
                }
                // Check if the marker is EOI or SOS.
                if (marker == 0xD9 || marker == 0xDA) {
                    break;
                }
    
                // Get the length and check if it is reasonable.
                length = pack(jpeg, offset, 2, false);
                if (length < 2 || offset + length > jpeg.length) {
                    Log.e(TAG, "Invalid length");
                    return 0;
                }
    
                // Break if the marker is EXIF in APP1.
                if (marker == 0xE1 && length >= 8 &&
                        pack(jpeg, offset + 2, 4, false) == 0x45786966 &&
                        pack(jpeg, offset + 6, 2, false) == 0) {
                    offset += 8;
                    length -= 8;
                    break;
                }
    
                // Skip other markers.
                offset += length;
                length = 0;
            }
    
            // JEITA CP-3451 Exif Version 2.2
            if (length > 8) {
                // Identify the byte order.
                int tag = pack(jpeg, offset, 4, false);
                if (tag != 0x49492A00 && tag != 0x4D4D002A) {
                    Log.e(TAG, "Invalid byte order");
                    return 0;
                }
                boolean littleEndian = (tag == 0x49492A00);
    
                // Get the offset and check if it is reasonable.
                int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
                if (count < 10 || count > length) {
                    Log.e(TAG, "Invalid offset");
                    return 0;
                }
                offset += count;
                length -= count;
    
                // Get the count and go through all the elements.
                count = pack(jpeg, offset - 2, 2, littleEndian);
                while (count-- > 0 && length >= 12) {
                    // Get the tag and check if it is orientation.
                    tag = pack(jpeg, offset, 2, littleEndian);
                    if (tag == 0x0112) {
                        // We do not really care about type and count, do we?
                        int orientation = pack(jpeg, offset + 8, 2, littleEndian);
                        switch (orientation) {
                            case 1:
                                return 0;
                            case 3:
                                return 3;
                            case 6:
                                return 6;
                            case 8:
                                return 8;
                        }
                        Log.i(TAG, "Unsupported orientation");
                        return 0;
                    }
                    offset += 12;
                    length -= 12;
                }
            }
    
            Log.i(TAG, "Orientation not found");
            return 0;
        }
    
        private static int pack(byte[] bytes, int offset, int length,
                boolean littleEndian) {
            int step = 1;
            if (littleEndian) {
                offset += length - 1;
                step = -1;
            }
    
            int value = 0;
            while (length-- > 0) {
                value = (value << 8) | (bytes[offset] & 0xFF);
                offset += step;
            }
            return value;
        }
    }
    

    rotateImage function :

        private Bitmap rotateImage(Bitmap bm, int i) {
            Matrix matrix = new Matrix();
            switch (i) {
                case ExifInterface.ORIENTATION_NORMAL:
                    return bm;
                case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
                    matrix.setScale(-1, 1);
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    matrix.setRotate(180);
                    break;
                case ExifInterface.ORIENTATION_FLIP_VERTICAL:
                    matrix.setRotate(180);
                    matrix.postScale(-1, 1);
                    break;
                case ExifInterface.ORIENTATION_TRANSPOSE:
                    matrix.setRotate(90);
                    matrix.postScale(-1, 1);
                    break;
                case ExifInterface.ORIENTATION_ROTATE_90:
                    matrix.setRotate(90);
                    break;
                case ExifInterface.ORIENTATION_TRANSVERSE:
                    matrix.setRotate(-90);
                    matrix.postScale(-1, 1);
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    matrix.setRotate(-90);
                    break;
                default:
                    return bm;
            }
            try {
                Bitmap bmRotated = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
                bm.recycle();
                return bmRotated;
            } catch (OutOfMemoryError e) {
                e.printStackTrace();
                return null;
            }
        }