简体   繁体   English

如何在帧中实时加入带有 alpha / 透明度的 png

[英]How to join png with alpha / transparency in a frame in realtime

I'm working under the example of OpenCV android 2.4.11 which detects faces using the camera.我在 OpenCV android 2.4.11 的例子下工作,它使用相机检测人脸。 Instead of drawing a rectangle on the face found, I'm trying to put a mask (png image) on the face.我没有在发现的脸上画一个矩形,而是试图在脸上放一个面具(png 图像)。 But to display the image on the face, the png image is coming with a black background where there was transparency.但是为了在脸上显示图像,png 图像带有黑色背景,其中有透明度。

FdActivity.java FdActivity.java

public void onCameraViewStarted(int width, int height) {
        mGray = new Mat();
        mRgba = new Mat();

        //Load my mask png
        Bitmap image = BitmapFactory.decodeResource(getResources(), R.drawable.mask_1);

        mask = new Mat();

        Utils.bitmapToMat(image, mask);

}

public Mat onCameraFrame(CvCameraViewFrame inputFrame) {

        mRgba = inputFrame.rgba();
        mGray = inputFrame.gray();

        if (mAbsoluteFaceSize == 0) {
            int height = mGray.rows();
            if (Math.round(height * mRelativeFaceSize) > 0) {
                mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize);
            }
            mNativeDetector.setMinFaceSize(mAbsoluteFaceSize);
        }

        MatOfRect faces = new MatOfRect();

        if (mDetectorType == JAVA_DETECTOR) {
            if (mJavaDetector != null)
                mJavaDetector.detectMultiScale(mGray, faces, 1.1, 2, 2,
                        new Size(mAbsoluteFaceSize, mAbsoluteFaceSize), new Size());
        }
        else if (mDetectorType == NATIVE_DETECTOR) {
            if (mNativeDetector != null)
                mNativeDetector.detect(mGray, faces);
        }
        else {
            Log.e(TAG, "Detection method is not selected!");
        }

        Rect[] facesArray = faces.toArray();


        for (int i = 0; i < facesArray.length; i++) {

              overlayImage(mRgba, mask, facesArray[i]);

        }

        return mRgba;
    }

    public Mat overlayImage(Mat background, Mat foregroundMask, Rect faceRect)
    {
        Mat mask = new Mat();

        Imgproc.resize(this.mask, mask, faceRect.size());

        Mat source = new Mat();
        Imgproc.resize(foregroundMask, source, background.size());

        mask.copyTo( background.submat( new Rect((int) faceRect.tl().x, (int) faceRect.tl().y, mask.cols(), mask.rows())) );

        source.release();
        mask.release();
        return background;
    }

Note: I will explain the general principle and give you an example implementation in Python, as I don't have the Android development environment set up.注意:由于我没有设置Android开发环境,我将解释一般原理并提供Python中的示例实现。 It should be fairly straightforward to port this to Java.将其移植到 Java 应该相当简单。 Feel free to post your code as a separate answer.随意将您的代码作为单独的答案发布。


You need to do something similar to what the addWeighted operation does, that is the operation你需要做一些类似于addWeighted操作所做的事情,那就是操作

线性混合公式

However, in your case, α needs to be a matrix (ie we need a different blending coefficient per pixel).但是,在您的情况下, α 需要是一个矩阵(即我们需要每个像素不同的混合系数)。


Sample Images示例图像

Let's use some sample images to illustrate this.让我们使用一些示例图像来说明这一点。 We can use the Lena image as a sample face:我们可以使用 Lena 图像作为样本人脸:

样脸

This image as an overlay with transparency:此图像作为具有透明度的叠加层:

与 Alpha 叠加

And this image as an overlay without transparency:这张图片是没有透明度的叠加层:

无 Alpha 叠加


Blending Matrix混合矩阵

To obtain the alpha matrix, we can either determine the foreground (overlay) and background (the face) masks using thresholding, or use the alpha channel from the input image if this is available.为了获得alpha矩阵,我们可以使用阈值确定前景(覆盖)和背景(人脸)蒙版,或者使用来自输入图像的 alpha 通道(如果可用)。

It is useful to perform this on floating point images with values in range 0.0 .. 1.0.在值范围为 0.0 .. 1.0 的浮点图像上执行此操作很有用。 We can then express the relationship between the two masks as然后我们可以将两个掩码之间的关系表示为

foreground_mask = 1.0 - background_mask

ie the two masks added together result in all ones.即两个掩码加在一起的结果是所有的。

For the overlay image in RGBA format we get the following foreground and background masks:对于 RGBA 格式的叠加图像,我们得到以下前景和背景蒙版:

来自透明度的前景蒙版

来自透明度的背景蒙版

When we use thresholding, erode and blur in case of RGB format, we get the following foreground and background masks:当我们在 RGB 格式的情况下使用阈值、侵蚀和模糊时,我们得到以下前景和背景蒙版:

来自阈值的前景蒙版

来自阈值的背景掩码


Weighted Sum加权总和

Now we can calculate two weighted parts:现在我们可以计算两个加权部分:

foreground_part = overlay_image * foreground_mask
background_part = face_image * background_mask

For RGBA overlay the foreground and background parts look as follows:对于 RGBA 叠加,前景和背景部分如下所示:

前景部分(RGBA 叠加)

背景部分(RGBA 叠加)

And for RGB overlay the foreground and background parts look as such:对于 RGB 叠加,前景和背景部分如下所示:

前景部分(RGB叠加)

背景部分(RGB叠加)


And finally add them together, and convert the image back to 8bit integers in range 0-255.最后将它们加在一起,并将图像转换回 0-255 范围内的 8 位整数。

The result of the operations looks as follows (RGBA and RGB overlay respectively):运算结果如下(分别为RGBA和RGB叠加):

合并(RGBA 叠加)

合并(RGB 叠加)


Code Sample - RGB Overlay代码示例 - RGB 叠加

import numpy as np
import cv2

# ==============================================================================

def blend_non_transparent(face_img, overlay_img):
    # Let's find a mask covering all the non-black (foreground) pixels
    # NB: We need to do this on grayscale version of the image
    gray_overlay = cv2.cvtColor(overlay_img, cv2.COLOR_BGR2GRAY)
    overlay_mask = cv2.threshold(gray_overlay, 1, 255, cv2.THRESH_BINARY)[1]

    # Let's shrink and blur it a little to make the transitions smoother...
    overlay_mask = cv2.erode(overlay_mask, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)))
    overlay_mask = cv2.blur(overlay_mask, (3, 3))

    # And the inverse mask, that covers all the black (background) pixels
    background_mask = 255 - overlay_mask

    # Turn the masks into three channel, so we can use them as weights
    overlay_mask = cv2.cvtColor(overlay_mask, cv2.COLOR_GRAY2BGR)
    background_mask = cv2.cvtColor(background_mask, cv2.COLOR_GRAY2BGR)

    # Create a masked out face image, and masked out overlay
    # We convert the images to floating point in range 0.0 - 1.0
    face_part = (face_img * (1 / 255.0)) * (background_mask * (1 / 255.0))
    overlay_part = (overlay_img * (1 / 255.0)) * (overlay_mask * (1 / 255.0))

    # And finally just add them together, and rescale it back to an 8bit integer image
    return np.uint8(cv2.addWeighted(face_part, 255.0, overlay_part, 255.0, 0.0))

# ==============================================================================

# We load the images
face_img = cv2.imread("lena.png", -1)
overlay_img = cv2.imread("overlay.png", -1)

result_1 = blend_non_transparent(face_img, overlay_img)
cv2.imwrite("merged.png", result_1)

Code Sample - RGBA Overlay代码示例 - RGBA 叠加

import numpy as np
import cv2

# ==============================================================================

def blend_transparent(face_img, overlay_t_img):
    # Split out the transparency mask from the colour info
    overlay_img = overlay_t_img[:,:,:3] # Grab the BRG planes
    overlay_mask = overlay_t_img[:,:,3:]  # And the alpha plane

    # Again calculate the inverse mask
    background_mask = 255 - overlay_mask

    # Turn the masks into three channel, so we can use them as weights
    overlay_mask = cv2.cvtColor(overlay_mask, cv2.COLOR_GRAY2BGR)
    background_mask = cv2.cvtColor(background_mask, cv2.COLOR_GRAY2BGR)

    # Create a masked out face image, and masked out overlay
    # We convert the images to floating point in range 0.0 - 1.0
    face_part = (face_img * (1 / 255.0)) * (background_mask * (1 / 255.0))
    overlay_part = (overlay_img * (1 / 255.0)) * (overlay_mask * (1 / 255.0))

    # And finally just add them together, and rescale it back to an 8bit integer image    
    return np.uint8(cv2.addWeighted(face_part, 255.0, overlay_part, 255.0, 0.0))

# ==============================================================================

# We load the images
face_img = cv2.imread("lena.png", -1)
overlay_t_img = cv2.imread("overlay_transparent.png", -1) # Load with transparency

result_2 = blend_transparent(face_img, overlay_t_img)
cv2.imwrite("merged_transparent.png", result_2)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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