Android:代码撸彩妆 2(大眼,瘦脸,大长腿)

序言

本篇文章是代码撸彩妆的第二篇, 主要介绍在Android上怎么进行图片的局部变形,并实现抖音上比较火的大眼,瘦脸,大长腿特效.

在开始之前我们先来回顾上一篇的主要内容.
使用代码画一半的效果如下

public enum Region {
   

    FOUNDATION("粉底"),
    BLUSH("腮红"),
    LIP("唇彩"),
    BROW("眉毛"),

    EYE_LASH("睫毛"),
    EYE_CONTACT("美瞳"),
    EYE_DOUBLE("双眼皮"),
    EYE_LINE("眼线"),
    EYE_SHADOW("眼影");

    private String name;
    Region(String name) {
   
        this.name = name;
    }
}

使用代码画出各种效果. 上一篇的文章地址 Android:让你的“女神”逆袭,代码撸彩妆(画妆)

上一篇和本篇的代码所在地址一致,都已经托管到github,如果你喜欢,欢迎给一个star,谢谢 https://github.com/DingProg/Makeup

现在开始我们今天的主题,人体(图像)的局部变形,如果要直接看效果的话,可以点击目录快速滑到效果区域.

大眼

效果

实现

图片局部缩放原理

我们知道,图片的放大缩小,是比较容易的事,相应的库已经封装好了,可以直接使用(我们并不需要关注图形放大缩小的插值处理等). 但是图片的局部放大缩小,并没有直接封装好,比如Android里面的bitmap,并没有直接局部处理放大缩小的API.

那我们先来看一下什么是图形的局部缩放?

局部的缩放,我们可以想象成中心点被缩放的比例比较小,而边缘的地方被缩放的比例很小,或者边界区域几乎没有变化,这样就可以达到一种平滑的效果。如果直接只对选中的圆形区域,变化的话,那边缘就变成了断裂式的缩放.

借用1993年的一篇博士论文 Interactive Image Warping 对局部图片进行缩放


其中a为缩放因子,当a=0时,不缩放

代码实现

既然要让眼睛放大,那么我们就把对应的近圆心的点的值️赋给远心点。
按照论文里所提到的思路,进行部分修改,实现如下.

  /**
     *  眼睛放大算法
     * @param bitmap      原来的bitmap
     * @param centerPoint 放大中心点
     * @param radius      放大半径
     * @param sizeLevel    放大力度  [0,4]
     * @return 放大眼睛后的图片
     */
    public static Bitmap magnifyEye(Bitmap bitmap, Point centerPoint, int radius, float sizeLevel) {
   
        TimeAopUtils.start();
        Bitmap dstBitmap = bitmap.copy(Bitmap.Config.RGB_565, true);
        int left = centerPoint.x - radius < 0 ? 0 : centerPoint.x - radius;
        int top = centerPoint.y - radius < 0 ? 0 : centerPoint.y - radius;
        int right = centerPoint.x + radius > bitmap.getWidth() ? bitmap.getWidth() - 1 : centerPoint.x + radius;
        int bottom = centerPoint.y + radius > bitmap.getHeight() ? bitmap.getHeight() - 1 : centerPoint.y + radius;
        int powRadius = radius * radius;

        int offsetX, offsetY, powDistance, powOffsetX, powOffsetY;

        int disX, disY;

        //当为负数时,为缩小
        float strength = (5 + sizeLevel * 2) / 10;

        for (int i = top; i <= bottom; i++) {
   
            offsetY = i - centerPoint.y;
            for (int j = left; j <= right; j++) {
   
                offsetX = j - centerPoint.x;
                powOffsetX = offsetX * offsetX;
                powOffsetY = offsetY * offsetY;
                powDistance = powOffsetX + powOffsetY;

                if (powDistance <= powRadius) {
   
                    double distance = Math.sqrt(powDistance);
                    double sinA = offsetX / distance;
                    double cosA = offsetY / distance;

                    double scaleFactor = distance / radius - 1;
                    scaleFactor = (1 - scaleFactor * scaleFactor * (distance / radius) * strength);

                    distance = distance * scaleFactor;
                    disY = (int) (distance * cosA + centerPoint.y + 0.5);
                    disY = checkY(disY, bitmap);
                    disX = (int) (distance * sinA + centerPoint.x + 0.5);
                    disX = checkX(disX, bitmap);
                    //中心点不做处理
                    if (!(j == centerPoint.x && i == centerPoint.y)) {
   
                        dstBitmap.setPixel(j, i, bitmap.getPixel(disX, disY));
                    }
                }
            }
        }
        TimeAopUtils.end("eye","magnifyEye");
        return dstBitmap;
    }

    private static int checkY(int disY, Bitmap bitmap) {
   
        if (disY < 0) {
   
            disY = 0;
        } else if (disY >= bitmap.getHeight()) {
   
            disY = bitmap.getHeight() - 1;
        }
        return disY;
    }

    private static int checkX(int disX, Bitmap bitmap) {
   
        if (disX < 0) {
   
            disX = 0;
        } else if (disX >= bitmap.getWidth()) {
   
            disX = bitmap.getWidth() - 1;
        }
        return disX;
    }

其中里面计算缩放前后后的点,使用的是如下图所示的计算规则计算.

有了这个方法,我们借助人脸识别的结果,把眼睛中心部分传入进去就可以实现自动大眼的效果了.

    Bitmap magnifyEye = MagnifyEyeUtils.magnifyEye(bitmap,
    Objects.requireNonNull(FacePoint.getLeftEyeCenter(faceJson)),
    FacePoint.getLeftEyeRadius(faceJson) * 3, 3);

略有不足

  • 代码所示部分没有使用插值 (代码直接使用了值替代,而不是
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值