IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 深度学习项目实践——制作一个能一键更换证件照背景的软件 -> 正文阅读

[人工智能]深度学习项目实践——制作一个能一键更换证件照背景的软件

背景

前段时间找工作,做简历的时候看中了一个红色为底色的模板,但是发现我的证件照只有蓝色底色的,这和简历不搭啊!有点强迫症的我怎么能忍!果断上网找方法换证件照底色(不要问我为什么不去重新拍一张,因为我是个节(pin)俭(qiong)的人),几经周折,终于在支付宝上找到了换证件照背景的应用,然而。。。。。。

!!居然要钱?!作为一名资深白嫖党和工(diao)程(bao)师(xia)怎么能忍得了这个,那就两个字,撸它!

方案

比较常见且效果比较好的传统方法大致流程是这样的,用聚类的方法分离出背景色,然后背景与人物像素二值化,再结合高斯模糊和形态学上的一些处理,锐化人物边缘,使得边缘更加平和,如下所示,效果也是十分惊艳的

所以,问题就这么解决了?赶紧拿我自己的照片试试,然而结果是这样的

注意看眼镜腿那一块,因为是根据像素值来分割背景,所以一但身上的物品或衣服和原背景颜色相似,就容易出现上图的情况,其实应该也能解决,那就是先利用边缘检测的方法,分隔人物与背景,再用上述聚类的方法分离出背景色再进行替换,因为最近比较忙(lan),所以这里我就不尝试了。

接下来进入正题,既然要做,那功能就应该做的多一些对吧,不应该仅仅只能替换证件照的背景,复杂场景下的图片应该也能转换成证件照,那么问题来了,传统方法想实现复杂场景下的分割,才疏学浅的我真不知道怎么做了(卑微脸),如果有大佬有好的实现方法,记得指导一下哇!

最终我采用了 U2-Net 来作为分割网络,他是作者基于 Unet 提出的一种新的网络结构,同样基于 encode-decode,参考 FPN,Unet,在此基础之上提出了一种新模块 RSU(ReSidual U-blocks) 经过测试,对于分割物体前背景取得了惊人的效果,关于这个 RSU 模块,本身就类似于一个小号的 Unet 网络,然后作者通过类似于 FPN 的结构将多个小 Unet 的输出结果进行组合,最后合并得到 mask,同时通过多个 loss 在不同层的表现来进行模型参数的更新,这里贴一下它的模型结构

有兴趣的可以看一下原论文:https://arxiv.org/pdf/2005.09007.pdf 文章出来也比较早了,网上解读有很多,这里就不深入展开了。

来看一下实际的应用效果

很明显的看到边缘还有一层残留的蓝色背景,这和论文配图的效果不相符合啊

果然论文效果图看个热闹就好,但还好问题不是很大,加一些形态学的处理,再来看看效果

大功告成!
算法工作完成了,接下来就是 UI 界面了,对于 Python 玩家来说,Tkinter 和 PyQT 都是不错的 UI 工具,在这里我使用的是 Tkinter,以下是主程序代码

from tkinter import *
import tkinter as tk
from tkinter import ttk
import tkinter
from tkinter import messagebox
import threading
import cv2
import numpy as np
import torch
from tkinter.filedialog import *
import tkinter.colorchooser
from PIL import Image, ImageTk
from model import U2NET
import winreg

def get_desktop():
    key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
                          r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders',)
    return winreg.QueryValueEx(key, "Desktop")[0]

def normPRED(d):
    ma = torch.max(d)
    mi = torch.min(d)
    dn = (d-mi)/(ma-mi)
    return dn

def get_img(pic_path, width, height):
    pic = Image.open(pic_path).resize((width, height))
    pic = ImageTk.PhotoImage(pic)
    return pic


class Operate(object):
    def __init__(self):
        self.root = tkinter.Tk()
        self.root.title(u"OutBreak-Hui&证件照编辑")
        self.root.geometry("600x380")
        self.root.resizable(width=False, height=False)

        self.picDir = ""
        self.color = None
        self.save_height = None
        self.save_width = None
        self.save_img = None

        self.lab1 = Label(self.root, text=u"选择图片")
        self.entry1 = Entry(self.root, width=60)
        self.entry1.delete(0, "end")
        self.B1 = Button(self.root, text=u"浏览", command=self.show_pic, width=7)

        self.lab2 = Label(self.root, text=u"选择尺寸", width=43)
        self.var1 = tkinter.StringVar()
        self.combobox = tkinter.ttk.Combobox(self.root, textvariable=self.var1, value=(u"一寸", u"小一寸", u"大一寸",
                                                                                       u"二寸", u"五寸"), width=41)
        self.B2 = Button(self.root, text=u"选择背景", command=self.ChooseColor, width=43)
        self.B3 = Button(self.root, text=u"转   换&预   览", command=self.change_background, width=43)
        self.B4 = Button(self.root, text=u"保   存", command=self.save_pic, width=43)

        self.canva_show = Canvas(self.root, width=240, height=320, bg="white")

        global img
        img = get_img("select.png", 300, 100)
        self.lab3 = Label(self.root, image=img)

    def gui_arrang(self):
        self.lab1.place(x=20, y=10, anchor='nw')
        self.B1.place(x=529, y=7, anchor='nw')
        self.entry1.place(x=90, y=10, anchor='nw')

        self.canva_show.place(x=20, y=40, anchor='nw')

        self.B2.place(x=275, y=41, anchor='nw')

        self.lab2.place(x=275, y=81, anchor='nw')
        self.combobox.place(x=275, y=121, anchor='nw')

        self.B3.place(x=275, y=161, anchor='nw')

        self.B4.place(x=275, y=201, anchor='nw')

        self.lab3.place(x=275, y=251, anchor="nw")

    def get_pic_dir(self):
        default_dir = r"文件路径"
        self.picDir = askopenfilename(title=u"选择文件", initialdir=(os.path.expanduser(default_dir)))
        self.entry1.insert(0, str(self.picDir))
        return self.picDir

    def show_pic(self):
        global img_tk
        picDir = self.get_pic_dir()
        # img = cv2.imread(picDir)
        img = cv2.imdecode(np.fromfile(picDir, dtype=np.uint8), -1)
        # print(img)
        canvawidth = int(self.canva_show.winfo_reqwidth())
        canvaheight = int(self.canva_show.winfo_reqheight())

        img = cv2.resize(img, (canvawidth, canvaheight), interpolation=cv2.INTER_AREA)
        imgcv2 = cv2.cvtColor(img, cv2.COLOR_BGR2RGBA)
        current_img = Image.fromarray(imgcv2)
        img_tk = ImageTk.PhotoImage(image=current_img)
        self.canva_show.create_image(0, 0, anchor='nw', image=img_tk)

    def ChooseColor(self):
        r = tkinter.colorchooser.askcolor(title="颜色选择器")
        r = r[0]
        self.color = r
        # return r

    def thread_creat(self, func, *args):
        t = threading.Thread(target=func, args=args)
        t.setDaemon(True)
        t.start()

    def change_background(self):
        global img_tk
        pic_path = self.picDir
        model_name = "u2net"
        model_dir = "saved_models\\u2net\\u2net.pth"

        net = U2NET(3, 1)
        mean = [0.485, 0.456, 0.406]
        std = [0.229, 0.224, 0.225]

        if torch.cuda.is_available():
            net.load_state_dict(torch.load(model_dir))
            net.cuda()
        else:
            net.load_state_dict(torch.load(model_dir, map_location="cpu"))

        net.eval()

        pic = cv2.imdecode(np.fromfile(pic_path, dtype=np.uint8), -1)
        pic = cv2.cvtColor(pic, cv2.COLOR_BGR2RGB)
        pic_file = pic
        size = (pic.shape[1], pic.shape[0])

        pic = cv2.resize(pic, (320, 320)).astype(np.float32)
        pic /= 255.0
        pic = ((pic - mean) / std).astype(np.float32)
        pic = torch.from_numpy(pic.transpose(2, 0, 1)).unsqueeze(0)


        if torch.cuda.is_available():
            pic = pic.cuda()
        else:
            pass

        d1, d2, d3, d4, d5, d6, d7 = net(pic)
        pred = d1[:, 0, :, :]

        pred = normPRED(pred)

        predict = pred
        predict = predict.squeeze()
        predict_np = predict.cpu().data.numpy()

        kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
        predict_np = cv2.erode(predict_np, kernel)
        predict_np = cv2.GaussianBlur(predict_np, (3, 3), 0)

        im = Image.fromarray(predict_np * 255).convert('RGB')
        im = im.resize(size, resample=Image.BILINEAR)
        im = np.array(im)
        res = np.concatenate((pic_file, im[:, :, [0]]), -1)
        img = Image.fromarray(res.astype('uint8'), mode='RGBA')

        background = self.color
        color_list = list(background)
        color_list = [int(i) for i in color_list]
        background = tuple(color_list)

        base_image = Image.new("RGB", size, background)

        scope_map = np.array(img)[:, :, -1] / 255
        scope_map = scope_map[:, :, np.newaxis]
        scope_map = np.repeat(scope_map, repeats=3, axis=2)
        res_image = np.multiply(scope_map, np.array(img)[:, :, :3]) + np.multiply((1 - scope_map),
                                                                                  np.array(base_image))
        canvawidth = int(self.canva_show.winfo_reqwidth())
        canvaheight = int(self.canva_show.winfo_reqheight())
        self.save_img = Image.fromarray(np.uint8(res_image))
        res_image = cv2.resize(res_image, (canvawidth, canvaheight), interpolation=cv2.INTER_AREA)
        current_img = Image.fromarray(np.uint8(res_image))
        img_tk = ImageTk.PhotoImage(image=current_img)
        self.canva_show.create_image(0, 0, anchor='nw', image=img_tk)

    def save_pic(self):
        if self.picDir == "":
            tkinter.messagebox.showinfo("提示", "请选择图片")
        if self.combobox.get() == "一寸":
            self.save_height = 413
            self.save_width = 295
            self.save_img = self.save_img.resize((self.save_width, self.save_height), resample=Image.BILINEAR)
            out_path = os.sep.join([get_desktop(), "certificate.jpg"])
            self.save_img.save(out_path, quality=95, subsampling=0)
        elif self.combobox.get() == "小一寸":
            self.save_height = 390
            self.save_width = 260
            self.save_img = self.save_img.resize((self.save_width, self.save_height), resample=Image.BILINEAR)
            out_path = os.sep.join([get_desktop(), "certificate.jpg"])
            self.save_img.save(out_path, quality=95, subsampling=0)
        elif self.combobox.get() == "大一寸":
            self.save_height = 567
            self.save_width = 390
            self.save_img = self.save_img.resize((self.save_width, self.save_height), resample=Image.BILINEAR)
            out_path = os.sep.join([get_desktop(), "certificate.jpg"])
            self.save_img.save(out_path, quality=95, subsampling=0)
        elif self.combobox.get() == "二寸":
            self.save_height = 636
            self.save_width = 413
            self.save_img = self.save_img.resize((self.save_width, self.save_height), resample=Image.BILINEAR)
            out_path = os.sep.join([get_desktop(), "certificate.jpg"])
            self.save_img.save(out_path, quality=95, subsampling=0)
        elif self.combobox.get() == "五寸":
            self.save_height = 1200
            self.save_width = 840
            self.save_img = self.save_img.resize((self.save_width, self.save_height), resample=Image.BILINEAR)
            out_path = os.sep.join([get_desktop(), "certificate.jpg"])
            self.save_img.save(out_path, quality=95, subsampling=0)
        else:
            tkinter.messagebox.showinfo("提示", "请选择转换尺寸")


if __name__ == '__main__':
    O = Operate()
    O.gui_arrang()
    tkinter.mainloop()

最后比对一下支付宝上的收费应用的效果

收钱居然还有瑕疵?差点浪费我六块大洋

完整工程见GitHub:https://github.com/OutBreak-hui/IDPhoto
如果可以的话给个star,哈哈哈哈

在这里插入图片描述

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-09-22 14:41:05  更:2021-09-22 14:42:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/22 11:23:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码