应用体验:证件照一键生成

本项目主要是为了方便大家照不那么严格的证件照,纯自助,只要拿手机一拍,在“应用体验”页面上传,项目自动抠图和排版,再下载下来的就是符合背景色要求的证件照了。本项目只为方便大家,一些要求非常严格的证件照,还是建议在专业招照相馆或者自助机上进行。

整个项目使用步骤很简单:

  • 在“应用体验”页面拍照上传,要求露双肩。并根据证件照其它要求提前做好构图取景等。因为可以抠图换背景,所以不需要专业照相馆里的背景布
  • 选择合适的背景色,有红色、蓝色和白色三种颜色可选。然后点“提交图片”,系统就自动进行抠图换背景。
  • 选择合适的照片下载。处理后的照片显示出来,根据需求,点击合适的照片下载即可。

一、应用体验项目的问题分解

为了较快实现功能,选择了FastDeploy。就AIStudio的应用中心实现来推测,不太合适使用飞桨Serving,因此选型就剩下了FastDeploy、Hub以及Inference。其中前两个都封装的较好,属于开箱能用的。

1、实现基本功能,在notebook下跑通

  1. 拍照上传,使用AIStudio的上传功能
  2. 抠像,系统后台使用飞桨AI框架进行自动扣图。本项目最内核的部分就是这里了
  3. 后处理,主要是填背景色和证件照排版输出
    生成的图片,可以在AIStudio notebook下右键下载。

2、在本地应用中心调试跑通

BML CodeLab提供用户创建应用、调试应用和部署应用等功能。相关功能以Streamlit作为底层技术,为用户提供快速搭建交互式图形化界面的能力,只需要几行代码即可搭建一个炫酷的图形化界面。
文档参考:https://ai.baidu.com/ai-doc/AISTUDIO/Gktuwqf1x#应用

  • 按照应用中心例子,写应用代码。主要是页面展示
  • 将前面测试好的抠图和后处理代码,嵌入。
  • 本地“运行”调试,调试成功后部署即可。

在本项目中,为了更好的展示,也为了加快调试,使用了“默认图片”的设置,“运行”后直接点击“提交照片”即可测试。
会自动输出1寸照片和5寸6寸混排照片,选中合适的照片下载即可。

3、在服务端应用部署调试成功

可以使用“在浏览器打开”进行调试,但最终在部署后还需要调试,以免有任何遗漏,影响应用的体验。

二、在notebook下跑通

在学习FastDeploy、PaddleDet等多种套件模型后,选择FastDeploy里面的PP-Matting模型。具体文档见:https://gitee.com/paddlepaddle/FastDeploy/tree/develop/examples/vision/matting/ppmatting/python

1、安装FastDeploy

因最终“应用体验”使用的是cpu版本,所以这里最终安装cpu版本。

测试中发现cpu版本安装的时候较慢,尽管gpu版本文件1.68G,有时候反而安装速度要快于cpu版本。所以在调试的时候,大部分时候都是选择AIStudio高端版GPU版,FastDeploy也是安装的GPU版。因为飞桨面向用户的代码是不需要区分GPU和cpu,所以不管使用GPU还是cpu,除了处理速度不同外,开发者不会有任何不同的感受。

# !pip install fastdeploy-gpu-python -f https://www.paddlepaddle.org.cn/whl/fastdeploy.html
!pip install fastdeploy-python -f https://www.paddlepaddle.org.cn/whl/fastdeploy.html

2、准备PP-Matting模型

FastDeploy需要手动下载模型文件,从上面的文档中,可以看到下载语句。文档中是512 * 512大小模型,我这里选了1024* 1024图片的小的模型。

# 下面这句大约是PaddleSeg库里的抠图模型
# !cd ~/work/ && wget https://paddleseg.bj.bcebos.com/matting/models/deploy/pp-matting-hrnet_w18-human_1024.zip && unzip pp-matting-hrnet_w18-human_1024.zip
# fastdeploy库的模型
# !wget https://bj.bcebos.com/paddlehub/fastdeploy/PP-Matting-512.tgz && tar -xvf PP-Matting-512.tgz
!wget https://bj.bcebos.com/paddlehub/fastdeploy/PP-Matting-1024.tgz && tar -xvf PP-Matting-1024.tgz

3、抠图核心代码实现

PP-Matting API https://www.paddlepaddle.org.cn/fastdeploy-api-doc/python/html/matting.html

fastdeploy.vision.matting.PPMatting
class fastdeploy.vision.matting.PPMatting(model_file, params_file, config_file, runtime_option=None, model_format=<ModelFormat.PADDLE: 1>)[source]
Load a PPMatting model exported by PaddleSeg.

以下为手册中的例子,先跑通。然后学习和拆分核心代码,以移植到notebook下,并为最终“应用体验”部署做准备。

跑通PP-Matting例子

# #下载部署示例代码
!git clone https://github.com/PaddlePaddle/FastDeploy.git
%cd ~/FastDeploy/examples/vision/matting/ppmatting/python

# # 下载PP-Matting模型文件和测试图片
!wget https://bj.bcebos.com/paddlehub/fastdeploy/PP-Matting-512.tgz
!tar -xvf PP-Matting-512.tgz
!wget https://bj.bcebos.com/paddlehub/fastdeploy/matting_input.jpg
!wget https://bj.bcebos.com/paddlehub/fastdeploy/matting_bgr.jpg
# # CPU推理
!python infer.py --model PP-Matting-512 --image matting_input.jpg --bg matting_bgr.jpg --device cpu
# # GPU推理
# !python infer.py --model PP-Matting-512 --image matting_input.jpg --bg matting_bgr.jpg --device gpu
# # GPU上使用TensorRT推理 (注意:TensorRT推理第一次运行,有序列化模型的操作,有一定耗时,需要耐心等待)
# !python infer.py --model PP-Matting-512 --image matting_input.jpg --bg matting_bgr.jpg --device gpu --use_trt True
%cd ~/

将PP-Matting例子代码移植到notebook下

使用PP-Matting-1024模型进行抠图
https://gitee.com/paddlepaddle/FastDeploy/tree/develop/examples/vision/matting/ppmatting/python

# 下载1024模型
!wget https://bj.bcebos.com/paddlehub/fastdeploy/PP-Matting-1024.tgz
!tar -xvf PP-Matting-1024.tgz
# 将infer.py代码移植到notebook下执行
import fastdeploy as fd
import cv2
import os


# 配置runtime,加载模型
# runtime_option = build_option(args)
runtime_option = None
argsmodel = "/home/aistudio/PP-Matting-1024/"
model_file = os.path.join(argsmodel, "model.pdmodel")
params_file = os.path.join(argsmodel, "model.pdiparams")
config_file = os.path.join(argsmodel, "deploy.yaml")
# model = fd.vision.matting.PPMatting(
#     model_file, params_file, config_file, runtime_option=runtime_option)
model = fd.vision.matting.PPMatting(
    model_file, params_file, config_file)
    

抠图结束后,将其存为visualized_result_fg文件。

为了换背景色,先代码生成红、蓝和白三色图片。

# 写三张颜色背景图
import numpy as np 
x = np.ndarray([800, 600, 3])
x[:, :, :] = 0
x[:, :, 0] = 255
cv2.imwrite("blue.jpg", x)
x[:, :, :] = 0
x[:, :, 1] = 255
cv2.imwrite("green.jpg", x)
x[:, :, :] = 0
x[:, :, 2] = 255
cv2.imwrite("red.jpg", x)
x[:, :, :] = 255
cv2.imwrite("white.jpg", x)
# 预测图片抠图结果
%cd ~/

argsimage = "work/beautysmall.jpg"
argsbg = "beautysmall.jpg"
im = cv2.imread(argsimage)
# bg = cv2.imread(argsbg)
bgrgb = "red"
if bgrgb == "red":
    bgrgb = cv2.imread("red.jpg")
elif bgrgb == "blue":
    bgrgb = cv2.imread("blue.jpg")
else :
    bgrgb = cv2.imread("white.jpg")
bg = bgrgb
result = model.predict(im.copy())
# print(result)
# 可视化结果
vis_im = fd.vision.vis_matting(im, result)
vis_im_with_bg = fd.vision.swap_background_matting(im, bg, result)
# vis_im_with_red = fd.vision.swap_background_matting(im, bgrgb, result)
cv2.imwrite("visualized_result_fg.png", vis_im)
cv2.imwrite("visualized_result_replaced_bg.jpg", vis_im_with_bg)
print(
    "Visualized result save in ./visualized_result_replaced_bg.jpg and ./visualized_result_fg.jpg"
)
# vis_im_with_red.show()

可以点击生成的图片:visualized_result_replaced_bg.jpg,观察是否达到了预计的目标。

4、后处理

证件照的大小要求:

1、一寸照片尺寸大小为:2.5*3.5cm(厘米)。一寸照片用的太普遍了,我们常见的有学生证、健康证、工作证、中小学生教师资格证、驾驶证等。2、二寸照片尺寸大小为:3.5*4.5cm(厘米)。二寸主要用在部分公务员和部分国家的签证。中国护照、港澳通行证、台湾通行证、韩国以及澳大利亚签证、雅思考试、全国计算机等级考试、英语四六级考试等。

作者:兰花草
链接:https://www.zhihu.com/question/330460193/answer/723674520

zhege gengxiangxi
这个更详细
https://zhuanlan.zhihu.com/p/549253320

1寸 295px × 413px 25* 35
2寸 413px × 579px
5寸生活照片 89mm × 127mm 1050px × 1500px

针对1寸照片需求,我们需要把大小调整为295*413 。使用F.resize函数即可。

import numpy as np
from PIL import Image
from paddle.vision.transforms import functional as F

fake_img = (np.random.rand(256, 300, 3) * 255.).astype('uint8')

fake_img = Image.fromarray(fake_img)

converted_img = F.resize(fake_img, 224)
testim = F.tran 
testimg = F.resize(vis_im, (295, 413, 3))
print(converted_img.size)
print(testimg.size)

加白框

思路,可以把图片调整为290 * 408 ,然后再叠加到295 * 413
也可以采用在295 * 413照片上画白线来解决。倾向于第二个方法。
弄明白了,单照片不用放边框。

平铺以便打印

5寸 8.9* 12.7 0.7
6寸 10.2* 15.2 0.67
大6寸 114* 15.2 0.75

这样5寸可以打9张 ,3列3排
295* 3 = 885 2.5* 3 = 7.5
413* 3 = 1239 3.5* 3=10.5

这样可以有足够的位置放白框。纵横间隔为40像素,这样打印照片的大小为:
295* 3+40* 4= 1045
413* 3 +40* 4 =1399 约等于1400 ,这样打印的时候有一定的盈余。

5、后处理实践

因为比较复杂,所以单独列一项出来。

几个技术,经过比较,后来决定使用csnd上现成的代码。
以下是中间走过的弯路

Pad增加边界

class paddle.vision.transforms.Pad(padding, fill=0, padding_mode=‘constant’, keys=None)

img (PIL.Image|np.ndarray|Paddle.Tensor) - 输入的图像数据,数据格式为’HWC’。

output (PIL.Image|np.ndarray|Paddle.Tensor) - 返回填充后的图像数据。

padding (int|list|tuple) - 在图像边界上进行填充的范围。如果提供的是单个 int 值,则该值用于填充图像所有边;如果提供的是长度为 2 的元组/列表,则分别为图像左/右和顶部/底部进行填充;如果提供的是长度为 4 的元组/列表,则按照左,上,右和下的顺序为图像填充。

fill (int|list|tuple,可选) - 用于填充的像素值。仅当 padding_mode 为 constant 时参数值有效。 默认值:0。 如果参数值是一个长度为 3 的元组,则会分别用于填充 R,G,B 通道。

Resize调整大小

class paddle.vision.transforms.Resize(size, interpolation=‘bilinear’, keys=None)[源代码]
将输入数据调整为指定大小。

需要调整称2.5:3.5 也就是5:7的比例,这样原图cai bu bi

CenterCrop

class paddle.vision.transforms.CenterCrop(size, keys=None)[源代码]
对输入图像进行裁剪,保持图片中心点不变。

主要是调整成5:7的比例

csdn学到的代码

这段代码不错。 拿来用了,来源于csdn论坛:http://t.csdn.cn/8UhHo

# Author:ZM
# http://t.csdn.cn/8UhHo

"""
照片尺寸,宽*高(单位:像素)
1寸照片:295*413
2寸照片:413*626
5寸照片(横版):1500*1050
6寸照片(横版):1800*1200
"""
from PIL import Image,ImageDraw

WIDTH_1IN = 295
HEIGHT_1IN = 413

WIDTH_2IN = 413
HEIGHT_2IN = 626

WIDTH_5IN = 1500
HEIGHT_5IN = 1050

# 非全景6寸照片
WIDTH_6IN = 1950
HEIGHT_6IN = 1300

def cut_photo(photo,choice):
    """
    将照片按照比例进行裁剪成1寸、2寸
    :param photo: 待处理的照片
    :param choice: <int> 1代表1寸,2代表2寸
    :return: 处理后的照片
    """
    width = photo.size[0] # 宽
    height = photo.size[1] #高
    rate = height / width
    if choice == 1:
        if rate < (HEIGHT_1IN/WIDTH_1IN):
            x = (width - int(height / HEIGHT_1IN * WIDTH_1IN)) / 2
            y = 0
            cutted_photo = photo.crop((x, y, x + (int(height / HEIGHT_1IN * WIDTH_1IN)), y + height))

        else:
            x = 0
            y = (height - int(width / WIDTH_1IN * HEIGHT_1IN)) / 2
            cutted_photo = photo.crop((x, y, x + width, y + (int(width / WIDTH_1IN * HEIGHT_1IN))))
        return cutted_photo

    if choice == 2:
        if rate < (HEIGHT_2IN/WIDTH_2IN):
            x = (width - int(height / HEIGHT_2IN * WIDTH_2IN)) / 2
            y = 0
            cutted_photo = im.crop((x, y, x + (int(height / HEIGHT_2IN * WIDTH_2IN)), y + height))

        else:
            x = 0
            y = (height - int(width / WIDTH_2IN * HEIGHT_2IN)) / 2
            cutted_photo = im.crop((x, y, x + width, y + (int(width / WIDTH_2IN * HEIGHT_2IN))))

        return cutted_photo

def resize_photo(photo,choice):
    '''
    缩放照片
    :param photo: 待处理的照片
    :param choice: <int> 1代表1寸,2代表2寸
    :return: 处理后的照片
    '''
    if choice == 1:
        resized_photo = photo.resize((WIDTH_1IN,HEIGHT_1IN))
        return resized_photo
    if choice == 2:
        resized_photo = photo.resize((WIDTH_2IN, HEIGHT_2IN))
        return resized_photo


def layout_photo_5_1(photo):
    """
    在5寸照片上排版1寸照片
    :param photo: 待处理照片1寸
    :return: 处理后的照片
    """
    bk = Image.new("RGB", [WIDTH_5IN,HEIGHT_5IN], (255,255,255))
    draw = ImageDraw.Draw(bk)# 创建画笔
    draw.line([(0,HEIGHT_5IN/2),(WIDTH_5IN,HEIGHT_5IN/2)],fill=128) # 横线
    draw.line([(WIDTH_5IN*0.25,0),(WIDTH_5IN*0.25,HEIGHT_5IN)],fill=128) # 第1条竖线
    draw.line([(WIDTH_5IN*0.5,0),(WIDTH_5IN*0.5,HEIGHT_5IN)],fill=128) # 第2条竖线
    draw.line([(WIDTH_5IN*0.75,0),(WIDTH_5IN*0.75,HEIGHT_5IN)],fill=128) # 第3条竖线

    focus_point = [0.125 * WIDTH_5IN,0.25 * HEIGHT_5IN]
    start_point = [focus_point[0] - 0.5 * WIDTH_1IN, focus_point[1] - 0.5 * HEIGHT_1IN]
    for i in range(0,2):
        for k in range(0,4):
            bk.paste(photo, (int(start_point[0] + (k * WIDTH_5IN / 4)), int(start_point[1] + 0.5 * i * HEIGHT_5IN)))
    return bk


def layout_photo_5_2(photo):
    """
    在5寸照片上排版2寸照片
    :param photo: 待处理照片2寸
    :return: 处理后的照片
    """
    bk = Image.new("RGB", [HEIGHT_5IN,WIDTH_5IN], (255,255,255)) # 竖版排版
    # 创建画笔
    draw = ImageDraw.Draw(bk)
    draw.line([(0,WIDTH_5IN/2),(WIDTH_5IN,WIDTH_5IN/2)],fill=128) # 横线
    draw.line([(HEIGHT_5IN*0.5,0),(HEIGHT_5IN*0.5,WIDTH_5IN)],fill=128) # 竖线
    focus_point = [0.25 * HEIGHT_5IN, 0.25 * WIDTH_5IN]
    start_point = [focus_point[0] - 0.5 * WIDTH_2IN, focus_point[1] - 0.5 * HEIGHT_2IN]
    #print(focus_point,start_point)
    for i in range(0,2):
        for k in range(0,2):
            bk.paste(photo, (int(start_point[0] + (k * HEIGHT_5IN / 2)), int(start_point[1] + 0.5* i * WIDTH_5IN)))
    return bk

def layout_photo_5_mix(photo1,photo2):
    """
    在5寸照片上混合排版1寸、2寸照片
    :param photo1: 待处理照片1寸
    :param photo1: 待处理照片2寸
    :return: 处理后的照片
    """
    bk = Image.new("RGB", [WIDTH_5IN,HEIGHT_5IN], (255,255,255))
    # 创建画笔
    draw = ImageDraw.Draw(bk)
    draw.line([(0,HEIGHT_5IN/2),(WIDTH_5IN,HEIGHT_5IN/2)],fill=128) # 横线
    draw.line([(WIDTH_5IN*0.25,0),(WIDTH_5IN*0.25,HEIGHT_5IN)],fill=128) # 第1条竖线
    draw.line([(WIDTH_5IN*0.5,0),(WIDTH_5IN*0.5,HEIGHT_5IN)],fill=128) # 第2条竖线

    focus_point = [0.125 * WIDTH_5IN,0.25 * HEIGHT_5IN]
    start_point = [focus_point[0] - 0.5 * WIDTH_1IN, focus_point[1] - 0.5 * HEIGHT_1IN]
    focus_point2 = [0.75 * WIDTH_5IN, 0.25 * HEIGHT_5IN]
    start_point2 = [focus_point2[0] - 0.5 * HEIGHT_2IN, focus_point2[1] - 0.5 * WIDTH_2IN]

    for i in range(0,2):
        for k in range(0,2):
            bk.paste(photo1, (int(start_point[0] + (k * WIDTH_5IN / 4)), int(start_point[1] + 0.5 * i * HEIGHT_5IN)))

    bk.paste(photo2,(int(start_point2[0]),int(start_point2[1])))
    bk.paste(photo2,(int(start_point2[0]),int(start_point2[1] + 0.5 * HEIGHT_5IN)))
    return bk

def layout_photo_6_1(photo):
    """
    在6寸照片上排版2寸照片
    :param photo: 待处理照片1寸
    :return: 处理后的照片
    """
    bk = Image.new("RGB", [HEIGHT_6IN,WIDTH_6IN], (255,255,255)) # 竖版排版
    # 创建画笔
    draw = ImageDraw.Draw(bk)
    draw.line([(0,WIDTH_6IN*0.25),(WIDTH_6IN,WIDTH_6IN*0.25)],fill=128) # 横线
    draw.line([(0,WIDTH_6IN*0.5),(WIDTH_6IN,WIDTH_6IN*0.5)],fill=128) # 横线
    draw.line([(0,WIDTH_6IN*0.75),(WIDTH_6IN,WIDTH_6IN*0.75)],fill=128) # 横线
    draw.line([(HEIGHT_6IN*0.25,0),(HEIGHT_6IN*0.25,WIDTH_6IN)],fill=128) # 竖线
    draw.line([(HEIGHT_6IN*0.5,0),(HEIGHT_6IN*0.5,WIDTH_6IN)],fill=128) # 竖线
    draw.line([(HEIGHT_6IN*0.75,0),(HEIGHT_6IN*0.75,WIDTH_6IN)],fill=128) # 竖线
    focus_point = [0.125 * HEIGHT_6IN, 0.125 * WIDTH_6IN]
    start_point = [focus_point[0] - 0.5 * WIDTH_1IN, focus_point[1] - 0.5 * HEIGHT_1IN]
    #print(focus_point,start_point)
    for i in range(0,4):
        for k in range(0,4):
            bk.paste(photo, (int(start_point[0] + (k * HEIGHT_6IN / 4)), int(start_point[1] + i * 0.25 * WIDTH_6IN )))
    return bk

def layout_photo_6_2(photo):
    """
    在6寸照片上排版2寸照片
    :param photo: 待处理照片2寸
    :return: 处理后的照片
    """
    bk = Image.new("RGB", [WIDTH_6IN,HEIGHT_6IN], (255,255,255))
    # 创建画笔
    draw = ImageDraw.Draw(bk)
    draw.line([(0,HEIGHT_6IN/2),(WIDTH_6IN,HEIGHT_6IN/2)],fill=128) # 横线
    draw.line([(WIDTH_6IN*0.25,0),(WIDTH_6IN*0.25,HEIGHT_6IN)],fill=128) # 第1条竖线
    draw.line([(WIDTH_6IN*0.5,0),(WIDTH_6IN*0.5,HEIGHT_6IN)],fill=128) # 第2条竖线
    draw.line([(WIDTH_6IN*0.75,0),(WIDTH_6IN*0.75,HEIGHT_6IN)],fill=128) # 第3条竖线
    focus_point = [0.125 * WIDTH_6IN,0.25 * HEIGHT_6IN]
    start_point = [focus_point[0] - 0.5 * WIDTH_2IN, focus_point[1] - 0.5 * HEIGHT_2IN]
    for i in range(0,2):
        for k in range(0,4):
            bk.paste(photo, (int(start_point[0] + (k * WIDTH_6IN / 4)), int(start_point[1] + 0.5 * i * HEIGHT_6IN)))
    return bk


def layout_photo_6_mix1(photo1,photo2):
    """
    在6寸照片上混合排版1寸、2寸照片
    :param photo1: 待处理照片1寸
    :param photo1: 待处理照片2寸
    :return: 处理后的照片
    """
    bk = Image.new("RGB", [WIDTH_6IN,HEIGHT_6IN], (255,255,255))
    # 创建画笔
    draw = ImageDraw.Draw(bk)
    draw.line([(0,HEIGHT_6IN*0.5),(WIDTH_6IN,HEIGHT_6IN/2)],fill=128) # 横线
    draw.line([(0,HEIGHT_6IN*0.25),(WIDTH_6IN*0.5,HEIGHT_6IN*0.25)],fill=128) # 短横线
    draw.line([(0,HEIGHT_6IN*0.75),(WIDTH_6IN*0.5,HEIGHT_6IN*0.75)],fill=128) # 短横线
    draw.line([(WIDTH_6IN*0.25,0),(WIDTH_6IN*0.25,HEIGHT_6IN)],fill=128) # 第1条竖线
    draw.line([(WIDTH_6IN*0.5,0),(WIDTH_6IN*0.5,HEIGHT_6IN)],fill=128) # 第2条竖线
    draw.line([(WIDTH_6IN*0.75,0),(WIDTH_6IN*0.75,HEIGHT_6IN)],fill=128) # 第3条竖线
    focus_point = [0.125 * WIDTH_6IN, 0.125 * HEIGHT_6IN]
    start_point = [focus_point[0] - 0.5 * HEIGHT_1IN, focus_point[1] - 0.5 * WIDTH_1IN]
    for i in range(0,4):
        for k in range(0,2):
            bk.paste(photo1, (int(start_point[0] + (0.25 * k * WIDTH_6IN )), int(start_point[1] + 0.25 * i * HEIGHT_6IN)))
    focus_point2 = [0.625 * WIDTH_6IN, 0.25 * HEIGHT_6IN]
    start_point2 = [focus_point2[0] - 0.5 * WIDTH_2IN, focus_point2[1] - 0.5 * HEIGHT_2IN]
    for i in range(0,2):
        for k in range(0,2):
            bk.paste(photo2,(int(start_point2[0] + (0.25 * k * WIDTH_6IN)), int(start_point2[1] + 0.5 * i * HEIGHT_6IN)))
    # 显示图片
    # bk.show()
    return bk



def layout_photo_6_mix2(photo1,photo2):
    """
    在6寸照片上混合排版1寸、2寸照片
    :param photo1: 待处理照片1寸
    :param photo1: 待处理照片2寸
    :return: 处理后的照片
    """
    bk = Image.new("RGB", [HEIGHT_6IN,WIDTH_6IN], (255,255,255)) # 竖版排版
    # 创建画笔
    draw = ImageDraw.Draw(bk)

    draw.line([(350,0),(350,WIDTH_6IN)],fill=128) # 竖线
    draw.line([(700,0),(700,WIDTH_6IN)],fill=128) # 竖线


    draw.line([(0,WIDTH_6IN*0.25),(700,WIDTH_6IN*0.25)],fill=128) # 横线1
    draw.line([(0,WIDTH_6IN*0.5),(700,WIDTH_6IN*0.5)],fill=128) # 横线2
    draw.line([(0,WIDTH_6IN*0.75),(700,WIDTH_6IN*0.75)],fill=128) # 横线3
    draw.line([(700,WIDTH_6IN/3),(HEIGHT_6IN,WIDTH_6IN/3)],fill=128) # 横线4
    draw.line([(700,WIDTH_6IN*2/3),(HEIGHT_6IN,WIDTH_6IN*2/3)],fill=128) # 横线5

    focus_point = [0.5 * 350, 0.125 * WIDTH_6IN]
    start_point = [focus_point[0] - 0.5 * WIDTH_1IN, focus_point[1] - 0.5 * HEIGHT_1IN]

    #print(focus_point,start_point)
    for i in range(0,4):
        for k in range(0,2):
            bk.paste(photo1, (int(start_point[0] + (k * 350)), int(start_point[1] + i * 0.25 * WIDTH_6IN )))

    focus_point2 = [0.5 * HEIGHT_6IN+350,  WIDTH_6IN/6]
    start_point2 = [focus_point2[0] - 0.5 * WIDTH_2IN, focus_point2[1] - 0.5 * HEIGHT_2IN]
    for i in range(0,3):
        bk.paste(photo2, (int(start_point2[0]), int(start_point2[1] + i  * WIDTH_6IN /3)))
    return bk


im = Image.open('beautysmall.jpg')
width = im.size[0]
height = im.size[1]
rate = height / width
layout_photo_5_1(resize_photo(cut_photo(im,1),1)).save('5_1.jpg')
layout_photo_5_2(resize_photo(cut_photo(im,2),2)).save('5_2.jpg')
layout_photo_6_1(resize_photo(cut_photo(im,1),1)).save('6_1.jpg')
layout_photo_6_2(resize_photo(cut_photo(im,2),2)).save('6_2.jpg')
layout_photo_5_mix(resize_photo(cut_photo(im,1),1),resize_photo(cut_photo(im,2),2).rotate(90,expand=True)).save('5_1_mix.jpg')
layout_photo_6_mix1(resize_photo(cut_photo(im,1),1).rotate(90,expand=True),resize_photo(cut_photo(im,2),2)).save('6_mix1.jpg')
layout_photo_6_mix2(resize_photo(cut_photo(im,1),1),resize_photo(cut_photo(im,2),2)).save('6_mix2.jpg')

将该文件命名为printpic.py存盘到work目录,并调试调用。
发现每个函数需要单独import,否则报错。

from work.printpic import cut_photo
from work.printpic import resize_photo 
 
from work.printpic import layout_photo_5_1 
from work.printpic import layout_photo_5_2 
from work.printpic import layout_photo_6_1 
from work.printpic import layout_photo_6_2 
from work.printpic import layout_photo_5_mix 
from work.printpic import layout_photo_6_mix1
from work.printpic import layout_photo_6_mix2

from PIL import Image,ImageDraw
im = Image.open('visualized_result_replaced_bg.jpg')
layout_photo_5_1(resize_photo(cut_photo(im,1),1)).save('5_1.jpg')
layout_photo_5_2(resize_photo(cut_photo(im,2),2)).save('5_2.jpg')
layout_photo_6_1(resize_photo(cut_photo(im,1),1)).save('6_1.jpg')
layout_photo_6_2(resize_photo(cut_photo(im,2),2)).save('6_2.jpg')
layout_photo_5_mix(resize_photo(cut_photo(im,1),1),resize_photo(cut_photo(im,2),2).rotate(90,expand=True)).save('5_1_mix.jpg')
layout_photo_6_mix1(resize_photo(cut_photo(im,1),1).rotate(90,expand=True),resize_photo(cut_photo(im,2),2)).save('6_mix1.jpg')
layout_photo_6_mix2(resize_photo(cut_photo(im,1),1),resize_photo(cut_photo(im,2),2)).save('6_mix2.jpg')

6、使用PaddleSeg里面的pp-matting(尝试过,未入选)

# !git clone https://github.com/PaddlePaddle/PaddleSeg

# %%writefile pipinstall.sh
# cd PaddleSeg
# pip install -r requirements.txt
# pip install -e .
# cd Matting
# pip install -r requirements.txt
# !sh pipinstall.sh
# !cd ~/work/ && wget https://paddleseg.bj.bcebos.com/matting/models/deploy/pp-matting-hrnet_w18-human_1024.zip && unzip pp-matting-hrnet_w18-human_1024.zip

背景替换

export CUDA_VISIBLE_DEVICES=0
python bg_replace.py \
    --config configs/modnet/modnet-mobilenetv2.yml \
    --model_path output/best_model/model.pdparams \
    --image_path path/to/your/image \
    --background path/to/your/background/image \
    --save_dir ./output/results \
    --fg_estimate True

如模型需要trimap信息,需要通过--trimap_path传入trimap路径。

--background可以传入背景图片路劲,或选择(‘r’,‘g’,‘b’,‘w’)中的一种,代表红,绿,蓝,白背景, 若不提供则采用绿色作为背景。

--fg_estimate False 可关闭前景估计功能,可提升预测速度,但图像质量会有所降低

注意: --image_path必须是一张图片的具体路径。

你可以直接下载我们提供的模型进行背景替换。

# !cd PaddleSeg/Matting && python tools/bg_replace.py \
#     --config configs/modnet/modnet-mobilenetv2.yml \
#     --model_path ~/work/pp-matting-hrnet_w18-human_1024/model.pdiparams \
#     --image_path ~/work/beautysmall.jpg \
#     --background "b" \
#     --save_dir ~/work/ \
#     --fg_estimate True

三、应用创建工具

点击新建-应用创建工具,生成应用文件模版,我这里改名为:app.streamlit.py。刚开始看见满屏的代码,可能有点不知所措,可以参考这个项目大头贴拍照机:https://aistudio.baidu.com/aistudio/projectdetail/5045676

1、将前面跑通的notebook代码写入app.streamlit.py

主要是注意代码中涉及到目录文件的代码,因为最终部署的时候需要打包到一个文件夹中,所以没法像传统项目那样在根目录下放好几个目录。当然可以在打包目录里面放几个目录。这个项目中,我们把打包目录设定为~/work目录 。

2、本机运行测试

根据报错信息,哪里出错调哪里。

3、应用部署调试

因为使用了FastDeploy库,所以需要在打包目录写一个requirements.txt文件。
文件内容为:fastdeploy-python -f https://www.paddlepaddle.org.cn/whl/fastdeploy.html

调试

fastdeploy部署报错error: the following arguments are required: --recipe, --mode

usage: infer.py [-h] --recipe RECIPE --mode MODE [--queue_dir QUEUE_DIR]
                [--base BASE] [--docker_args DOCKER_ARGS]
infer.py: error: the following arguments are required: --recipe, --mode

修改成这样报错:

pwdpath = !pwd
!cd ~/FastDeploy/examples/vision/matting/ppmatting/python && python infer.py  --recipe $pwdpath --mode MODE --model PP-Matting-512 --image matting_input.jpg --bg matting_bgr.jpg --device cpu

报错

usage: infer.py [-h] --recipe RECIPE --mode MODE [--queue_dir QUEUE_DIR]
                [--base BASE] [--docker_args DOCKER_ARGS]
infer.py: error: unrecognized arguments: --model PP-Matting-512 --image matting_input.jpg --bg matting_bgr.jpg --device cpu

这样看全乱套了啊!

在按照手册安装好FastDeploy之后,终于正常了!
pip install fastdeploy-gpu-python -f https://www.paddlepaddle.org.cn/whl/fastdeploy.html

照片后处理程序报错

在notebook下正常,赚到命令行下就报错

aistudio@jupyter-209599-1243758:~/work$ python printpic.py 
Traceback (most recent call last):
  File "printpic.py", line 267, in <module>
    main(inputfile="testli.jpg")
  File "printpic.py", line 259, in main
    layout_photo_5_2(resize_photo(cut_photo(im,2),2)).save('5_2.jpg')
  File "printpic.py", line 52, in cut_photo
    cutted_photo = im.crop((x, y, x + (int(height / HEIGHT_2IN * WIDTH_2IN)), y + height))
NameError: name 'im' is not defined

刚开始以为是图片大小不合适导致的,后来发现是原代码有小bug,im确实没有在函数中定义,它使用了全局变量,在我将后处理程序放入main函数后,该变量就不是全局变量了,导致该bug暴露出来。
将第52行的im改成photo即可。 cutted_photo = photo.crop((x, y, x + (int(height / HEIGHT_2IN * WIDTH_2IN)), y + height))

期间在测试PP-Matting一运行就报错

用系统自带的代码测试一下,ok,参见手册:https://github.com/PaddlePaddle/FastDeploy/tree/develop/examples/vision/matting/ppmatting/python
于是查找前面的问题,发现是因为调用了错误的模型导致的的,应该是"/home/aistudio/work/PP-Matting-1024/",错误的用了work/pp-matting-hrnet_w18-human_1024

应用体验部署的时候报目录超过2G限制

只有一个100M大小的模型文件,其实没有超限,原来是应用体验部署只支持飞桨2.3.2版本,我的是2.4.0版本,所以会报错。
后来使用在python代码中下载模型的方式,绕过了这个问题。

No module named ‘keypoint’

Traceback (most recent call last):
  File "infer.py", line 8, in <module>
    from keypoint import FaceLandMark
ModuleNotFoundError: No module named 'keypoint'

经检查,发现是FastDeploy没有成功git clone下来,而根目录下正好有个infer.py文件,导致这种神奇的报错。

结束语

让我们荡起双桨,在AI的海洋乘风破浪!

飞桨官网:https://www.paddlepaddle.org.cn

因为水平有限,难免有不足之处,还请大家多多帮助。

作者:段春华, 网名skywalk 或 天马行空,济宁市极快软件科技有限公司的AI架构师,百度飞桨PPDE。

我在AI Studio上获得至尊等级,点亮10个徽章,来互关呀~ https://aistudio.baidu.com/aistudio/personalcenter/thirdview/141218

此文章为搬运
原项目链接

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐