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

本文最后更新于 2024年2月10日 凌晨

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

好耶

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

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

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

Emoji Kitchen

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

Emoji Kitchen

Demo使用

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

合成游戏机

合成降落伞🪂

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

合成信息

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

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

求生欲拉满

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

支持创作

制作教程不易,如果热心的小伙伴,想支持创作,可以加入我们的「爱发电」电圈(还可以解锁远程协助、好友位😃):

当然,也欢迎在B站或YouTube上关注我们:

更多:

总揽视频

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

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

  • 展示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的合成检录,右侧Emoji进行结果的筛选。点击中间的结果区可以进行合成后的Emoji下载:

使用效果

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

Emojimix By Tikolu

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

效果图如下:

EmojiMix Tikolu

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

检录Emoji表情

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

Emoji Tikolu使用Webp进行展示

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

进入烤箱

Emoji Kitchen React

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

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

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

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

同时,整理了Google 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请求

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

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

metajson字典

下滑可以发现更多惊喜:

下滑查看data内容

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

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

在CI/CD中,进行metadata.json的下载

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

解析原始Emoji

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

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

所以,再次回到我们的重磅选手
https://emojikitchen.dev/;这次我们不查看Github仓库,直接访问页面,并且打开 F12 开发者调试,查看页面内容:

查看页面内容

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

1
<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内容):

1
2
pattern = r'src="(.*?\.svg)"'
matches = re.findall(pattern, SVG_RAW)

最后下载的结果:

下载的SVG文件

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

1
2
3
4
5
6
7
8
<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自动调用浏览器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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)

自动扫描完整div

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

id为root的占位符

解析合成Emoji

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

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

初代版本metadata.json

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

直接暴露Emoji Kitchen地址

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

1
2
3
4
5
6
7
8
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内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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的合成,能给大家再增添一些欢乐。



两个表情合成出新表情:复刻Emoji Kitchen!详解算法实现!
https://www.mintimate.cn/2024/02/05/EmojiMixAlgorithm/
作者
Mintimate
发布于
2024年2月5日
许可协议