圣诞节到了,我偷偷瞄了一眼,身边人的头像很和谐,我觉得这不正常:圣诞老人没给你们发帽子么?

好吧,既然圣诞老人没给大家发帽子,那Serverless架构给大家发一顶帽子吧。

先来预览一下样子,百度随便找了一个图:

加上圣诞帽:

看完效果,来测试一下功能(如果就是想玩一玩,可以直接使用我的这个接口):

url:
http://service-8d3fi753-12567...
入参:pic,,string类型,原始图片的base64
出参:picture,string类型,戴帽子的图片base64

基本测试代码(Python3):



import base64
import urllib.request
import json

with open("test.png", 'rb') as f:
    image = f.read()
    image_base64 = str(base64.b64encode(image), encoding='utf-8')

url = "https://service-ly70xmyz-1256773370.sh.apigw.tencentcs.com/test/addChristmasHat"
data = {
    "pic": image_base64
}
picture = json.loads(urllib.request.urlopen(urllib.request.Request(url=url, data=json.dumps(data).encode("utf-8"))).read().decode("utf-8"))["picture"]

imgData = base64.b64decode(picture)
with open('output.png', 'wb') as f:
    f.write(imgData)

当然,有一些小伙伴可能想要把这个服务部署到自己的云函数上,那么就可以参考下文:

项目核心代码(Python3的函数,部署在云函数就可以):



import cv2
import dlib
import base64
import json


def addHat(img, hat_img):
    print("分离rgba通道,合成rgb三通道帽子图,a通道后面做mask用")
    r, g, b, a = cv2.split(hat_img)
    rgbHat = cv2.merge((r, g, b))

    print("dlib人脸关键点检测器,正脸检测")
    predictorPath = "shape_predictor_5_face_landmarks.dat"
    predictor = dlib.shape_predictor(predictorPath)
    detector = dlib.get_frontal_face_detector()
    dets = detector(img, 1)

    print("如果检测到人脸")
    if len(dets) > 0:
        for d in dets:
            x, y, w, h = d.left(), d.top(), d.right() - d.left(), d.bottom() - d.top()

            print("关键点检测,5个关键点")
            shape = predictor(img, d)

            print("选取左右眼眼角的点")
            point1 = shape.part(0)
            point2 = shape.part(2)

            print("求两点中心")
            eyes_center = ((point1.x + point2.x) // 2, (point1.y + point2.y) // 2)

            print("根据人脸大小调整帽子大小")
            factor = 1.5
            resizedHatH = int(round(rgbHat.shape[0] * w / rgbHat.shape[1] * factor))
            resizedHatW = int(round(rgbHat.shape[1] * w / rgbHat.shape[1] * factor))

            if resizedHatH > y:
                resizedHatH = y - 1

            print("根据人脸大小调整帽子大小")
            resizedHat = cv2.resize(rgbHat, (resizedHatW, resizedHatH))

            print("用alpha通道作为mask")
            mask = cv2.resize(a, (resizedHatW, resizedHatH))
            maskInv = cv2.bitwise_not(mask)

            print("帽子相对与人脸框上线的偏移量")
            dh = 0
            bgRoi = img[y + dh - resizedHatH:y + dh,
                    (eyes_center[0] - resizedHatW // 3):(eyes_center[0] + resizedHatW // 3 * 2)]

            print("原图ROI中提取放帽子的区域")
            bgRoi = bgRoi.astype(float)
            maskInv = cv2.merge((maskInv, maskInv, maskInv))
            alpha = maskInv.astype(float) / 255

            print("相乘之前保证两者大小一致(可能会由于四舍五入原因不一致)")
            alpha = cv2.resize(alpha, (bgRoi.shape[1], bgRoi.shape[0]))
            bg = cv2.multiply(alpha, bgRoi)
            bg = bg.astype('uint8')

            print("提取帽子区域")
            hat = cv2.bitwise_and(resizedHat, cv2.bitwise_not(maskInv))

            print("相加之前保证两者大小一致(可能会由于四舍五入原因不一致)")
            hat = cv2.resize(hat, (bgRoi.shape[1], bgRoi.shape[0]))
            print("两个ROI区域相加")
            addHat = cv2.add(bg, hat)

            print("把添加好帽子的区域放回原图")
            img[y + dh - resizedHatH:y + dh,
            (eyes_center[0] - resizedHatW // 3):(eyes_center[0] + resizedHatW // 3 * 2)] = addHat

            return img


def main_handler(event, context):
    try:
        print("将接收到的base64图像转为pic")
        imgData = base64.b64decode(json.loads(event["body"])["pic"])
        with open('/tmp/picture.png', 'wb') as f:
            f.write(imgData)

        print("读取帽子素材以及用户头像")
        hatImg = cv2.imread("hat.png", -1)
        userImg = cv2.imread("/tmp/picture.png")

        output = addHat(userImg, hatImg)
        cv2.imwrite("/tmp/output.jpg", output)

        print("读取头像进行返回给用户,以Base64返回")
        with open("/tmp/output.jpg", "rb") as f:
            base64Data =  str(base64.b64encode(f.read()), encoding='utf-8')

        return {
            "picture": base64Data
        }
    except Exception as e:
        return {
            "error": str(e)
        }

使用方法:

下载我打包好的文件:https://serverless-framework-...

解压出来:

打开命令行工具,进入到项目目录:

执行serverless --debug:

可能会唤起二维码登录,手机扫码登录就好:

部署成功:

此时,你的接口地址就是,返回给你的地址+/add_christmas_hat,例如我的返回地址是:http://service-n5ahp2w4-12567...
则接口地址就是:
http://service-n5ahp2w4-12567...

大家在自己的项目中,可以直接使用这个接口就好了。例如小程序中上传一个图片,发送给这个服务,或者web页面上传一个图片,发送到这个服务,就可以得到Serverless送给大家的圣诞帽了。



anycodes
7 声望8 粉丝

浙江大学软件工程硕士毕业,现腾讯科技Serverless架构后台研发,Serverless Framework中国开发者之一,先后开发维护Plugin和多个Components,著有图书《Serverless架构》,是Serverless忠实粉丝和“拓荒者”