likes
comments
collection
share

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

作者站长头像
站长
· 阅读数 51

新年快乐嗷。给大家分享一下Emoji合成,助力欢乐加倍!让我们看看两个Emoji表情,可以合成出什么新表情,丰富聊天内容,让对话更加有趣!

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

博客:www.mintimate.cn

Mintimate’s Blog,只为与你分享

Emoji Kitchen 是由 Google 键盘 Gboard 推出的功能。它允许用户将两个不同的 emoji 进行组合,创造出独特的表情符号。

用户可以在 Google 搜索中输入 Emoji Kitchen 来使用这个功能。生成的新表情以图片的形式呈现,用户可以方便地复制并粘贴到他们的聊天应用程序或社交媒体中。

可能相比一些工具,两个Emoji合成出新的EmojiMix,似乎并没有什么作用;但是就和表情包一样,单论本身,说起来是没有意义,但是却实打实地丰富了聊天内容,让对话更加有趣。

Emoji Kitchen

Emoji Kitchen 可以在Google的搜索引擎上搜索Emoji Kitchen,即可体验Emoji的合成:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

我们可以选定两个Emoji,合成出新的Emoji:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

它是如何做到图片合成的呢? 是现在很火的AIGC,由AI对两个Emoji进行合成么?

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

其实并不是,早在2019年底,就有Emoji Kitchen,那个时候,GPT-2刚刚发布,可以生成自然语言文本,但质量和多样性有限,远远不及现在的GPT3.5,更不用说是可以处理图片的AI了。

当然,不排除Google有算法,在2018年时候就使用图形处理,实现合成Emoji的AI;但是这种可能性很低,大概率还是设计师设计制作。

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

不过,在搜索引擎上使用,有一些不优雅,容易被其他搜索结果所干扰;更重要的是,中国大陆用户无法访问,我们可以试试第三方复刻版本的Emoji Mix。

总揽视频

部分东西,还是视频比较清晰。

这里做个视频,主要内容:

  • 展示Emoji合成效果;

  • 解释Emoji合成原理;

  • 介绍如何复刻和实现EmojiMix算法。

做教程视频不易(B站根本不会推荐引流),请务必一键三连嗷~

复刻Emoji Mix

其实有非常多的Emoji Kitchen复刻版本,为了区别于Google官方的Emoji Kitchen,这里我们粗略地都称作:Emoji Mix。一般Emoji Mix都是使用Emoji Kitchen的图片源…… 我使用Python爬取了Google Emoji Kitchen,发现累计到现在,大概有5w张图片,占用空间500MB,估计很多网站都不会专门存储…… 

比较常见的Emoji Mix有很多,我们枚举一些。

OnlineTool EmojiMix

哈哈,首先当然是我复刻的版本:

效果图如下:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

左侧进行Emoji的合成检录,右侧Emoji进行结果的筛选。点击中间的结果区可以进行合成后的Emoji下载:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

还有什么惊喜呢? 大家可以自己探索一下。

Emojimix By Tikolu

接下来的选手,相信大家也非常熟悉了:

效果图如下:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

EmojiMix Tikolu也是非常好用的Emoji Mix版本;点击左侧的搜索🔍按钮,可以进行检录:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

不过,或许是为了适配动态Webp原因。EmojiMix Tikolu只选取了含有动态Webp的Emoji进行展示和参与合成,没有动态Webp的就不进行展示和参与合成了。举个动态Webp的例子:fonts.gstatic.com/s/e/notoemo…

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

说实话,我一开始并没有接触Google的Emoji Kitchen,就是小伙伴发EmojiMix Tikolu的链接🔗给我,我玩了大半个下午…… 甚至,后来选择一张Emoji合成图片,作为自己的头像(后来觉得太阳光了,还是应该深沉一些就换掉了)。

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

Emoji Kitchen React

最后介绍一个重磅选手,实际上官方项目名字叫Emoji-kitchen,但是我为了和Google进行区分,并且它是使用React进行技术实现的,所以我这里就给它取个别名啦:

和上文一样,我们看看效果:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

是不是感觉和我的OnlineTool EmojiMix很相似? 没错,我就是看到这个项目后,发现这个项目使用React实现,而我使用Vue + Nuxt进行了复刻。

为什么说是重磅选手呢?其实是因为它还开源了:

同时,整理了Google Emoji Kitchen的直链文件:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

2024.02.09 这个大文件已经被移动到CI/CD 里再下载了,可以查看这次commit: Move large file download to CI/CD

如果大家想复刻和实现更好的Emoji Mix,可以参考这个项目。

Emoji合成请求

知道了那里可以体验到合成的Emoji;有没有小伙伴还是想知道两个Emoji,如何变成一个复合Emoji呢?

实际上,如果你查看Google Emoji Kitchen 页面的源码,你会发现:

  • 用于合成Emoji的原始Emoji,实际上是SVG格式的,而合成后的Emoji是PNG格式的。

  • SVG格式和PNG格式的Emoji文件名由Unicode的编号进行组成。

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

也就是,我们可以把它当作API地址,对其进行请求得到我们的Emoji合成图片。

如何获得Emoji的Unicode字典,并且判断那些Emoji相互组合,Google Emoji Kitchen有对应的Emoji合成图片呢?我们就可以使用重磅选手提供的metadata.json字典:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

下滑可以发现更多惊喜:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

data内,我们就可以看到哪两个Emoji组成可以合成新的Emoji。于是,我们就可以使用Python,对这个文件进行解析。

另外,正如上文所说,metadata.json已经被移动到CI/CD里,也就是在构建这个React项目并部署的时候,才会进行下载:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

所以,如果你想查看metadata.json,可以直接访问下载地址。

解析原始Emoji

首先是解析原版的Emoji,其实我们可以直接用metadata.json里面的knownSupportedEmoji进行数据提取。但是我另辟蹊径了一下……

既然我们是准备使用knownSupportedEmoji配合API地址,请求出Emoji的SVG文件。为什么我们不直接下载渲染好的Emoji SVG文件呢?

所以,再次回到我们的重磅选手

emojikitchen.dev/;这次我们不查看Github仓库,直接访问页面,并且打开 F12 开发者调试,查看页面内容:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

你可以发现,所有的Emoji SVG已经在页面的div内展示,并且每个元素包含SVG的地址:


<li class="MuiImageListItem-root css-kxftp1" style="height: auto; grid-column-end: span 1; grid-row-end: span 1;"><img loading="lazy" width="32px" height="32px" alt="stuck_out_tongue" src="https://raw.githubusercontent.com/googlefonts/noto-emoji/main/svg/emoji_u1f61b.svg" class="MuiImageListItem-img"></li>

很简单啦,我们只需要复制这个div,直接使用正则表达式提取所有链接并下载即可(SVG_RAW为div内容):


pattern = r'src="(.*?\.svg)"'

matches = re.findall(pattern, SVG_RAW)

最后下载的结果:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

在Vue上进行展示,我们需要对文件名字符串做一些处理,主要是根据长度,对©️ 和 ®️ 进行长度截取:


 <img loading="lazy" :alt="`Emoji_${item.item}`"

               :src="`/img/emoji/svg/emoji_u${item.item.split('-')

                        .filter((x) => x !== 'fe0f')

                        .map((x) => x.padStart(4, '0')) // Handle ©️ and ®️

                        .join('_')}.svg`"

               :class="!ignoreDisable && item.disabled ? 'opacity-30 cursor-not-allowed':'cursor-pointer hover:scale-150 transition-transform duration-150 ease-in-out'"

               class="w-8 rounded-lg hover:ring-2 hover:ring-blue-200 hover:bg-amber-50/10 cursor-pointer

                            hover:scale-150 transition-transform duration-150 ease-in-out"/>

当时这样似乎不是很智能,每次都要人手动访问网站,F12复制元素,能不能更加智能一些?免去手动打开浏览器的操作?

这个时候,我们就可以引入selenium,自动让Python自动调用浏览器:


from bs4 import BeautifulSoup

from selenium import webdriver

  


def scan_svg_raw():

    options = webdriver.ChromeOptions()

    options.add_argument("--headless")

    driver = webdriver.Chrome(options=options)

    driver.get("https://emojikitchen.dev/")

    # 等待5秒让页面完全渲染

    time.sleep(5)

    # 获取页面源代码

    page_source = driver.page_source

    # 关闭浏览器

    driver.quit()

    soup = BeautifulSoup(page_source, 'html.parser')

    # 找到第一个 class 为 "MuiBox-root css-1uithqi" 的 div

    target_div = soup.find('div', class_='MuiBox-root css-1uithqi')

    # 获取该 div 内的全部内容

    content = target_div.prettify()

    # 将内容写入文件

    with open('source/svg_raw.txt', 'w', encoding='utf-8') as file:

        file.write(content)

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

有小伙伴可能会问,为什么不用requests库呢? 这个就要说了,网站实际是React渲染的,在页面使用JavaScript加载完成前,页面只有一个<div id="root"></div>的占位符,并没有实际内容:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

解析合成Emoji

原始的Emoji已经解析完成,合成后的Emoji呢?这次就不要再“造轮子”,直接使用metadata.json里的内容即可,但是这里还是提一下我的方法。

最初metadata.json里的内容是这样的:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

可以看到,体积比现在50MB版本的小很多;可能是官方觉得,他们的服务器性能比较好,所以在新版本metadata.json内,直接暴露了合成后的Emoji Kitchen地址:

两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!

那么,我基于初代版本,是如何处理的呢?其实很简单:


def get_download_url(json_object):

    root_png_dir = 'pngs/' + json_object['date'] + '/'

    if not os.path.exists(root_png_dir):

        os.makedirs(root_png_dir)

    left = "-".join(["u" + part.lower() for part in json_object['leftEmoji'].split("-")])

    right = "-".join(["u" + part.lower() for part in json_object['rightEmoji'].split("-")])

    date = json_object['date']

    return f'https://www.gstatic.com/android/keyboard/emojikitchen/{date}/{left}/{left}_{right}.png'

是不是还算巧妙?

json_object作为输入,并从中提取dateleftEmojirightEmoji的值。然后,它根据这些值构建了一个用于下载的URL的文件路径。下载URL以格式化字符串的形式返回。

当然,面对新版本的metadata.json,我们可以直接获取JSON内的data内,每个对象combinations数组内的gStaticUrl内容:


def get_download_url_from_metadata():

    with open(JSON_FILE_FULL, 'r') as f:

        # 读取整个文件内容

        json_data = json.load(f)['data']

    # 遍历"data"内的对象

    for obj in json_data.values():

        # 获取每个对象的"combinations"数组

        combinations = obj['combinations']

        # 遍历"combinations"数组

        for combination in combinations:

            # 获取每个组合的"gStaticUrl"属性

            static_url = combination['gStaticUrl']

            # 添加下载任务到Aria2内进行多线程异步下载

            add_mission_to_aria2(static_url)

END

好啦,本次的Emoji Mix复刻介绍就到这里啦。

总的来说,解决了Emoji Kitchen的一些小痛点。虽然实现的过程可能比较麻烦,但是实现的结果可以让更多人体会到Emoji表情的内涵,也是挺不错的。与此同时,也是使用Python进行数据清洗的小小Demo。

大家新年快乐,希望Emoji的合成,能给大家再增添一些欢乐。

转载自:https://juejin.cn/post/7333021939358842920
评论
请登录