专栏首页秋风的笔记产品经理:喂那个前端,你从图片提取下主题色

产品经理:喂那个前端,你从图片提取下主题色

一起网易云 🍉

网易云音乐想必是大家很熟悉的一款 app 了,毕竟大家在深夜都会网抑云

开玩笑了,最近在网易云听歌时,发现了一个很有意思的特效:

就是切换歌曲时,会根据当前封面替换背景色。作为资深切图仔,我那该死的好奇心兜不住了,不行,我要去一探究竟。

首先我构思了很多它可能的实现方式:

  • 机器学习对图片进行色彩分析
  • 前端提取图片主色调,做渐变处理
  • 封面背景图做高斯模糊

对于第一种,他不在我的知识范围内,这里就不展开说明了 😂。

第二种的话,一般都是利用canvas来实现。

第三种相对来说,从技术层面来看,实现上是最为简单的。

做了猜测分析后,我默默打开了熟悉的 Chrome 控制台,打开了网易云音乐的源代码:

好家伙,果然是第三种实现方式。🤐

本来到这里,本文就该结束了。但之前也有朋友问过我如何对前端图片主题色进行提取的问题,正好之前也做过类似的需求,这里就展开做个说明吧。

我们这里以一个图片网站为例,来展示实际业务中应用较广的场景:

在弱网下,图片加载速度较慢,此时在图片完全加载之前,提取图片的主色调,然后填充为背景色。这样用户体验能有较大的提升。

那具体是怎么实现的呢?🤔

我们这里采用canvas来实现,具体分为三步:

  • 获取图片数据
  • 对图片数据进行处理
  • 对颜色列表排序

这里我们使用的测试图片为:

相对来说,主色调较为明显,也便于测试~

获取图片数据 🦊

我们知道图片是由一个个像素点组成的。通过 canvas 的getImageData()方法恰好可以获取图片的像素数据:

let imgObj = document.getElementById('yourId');

// 创建画布
let canvas = document.createElement('canvas');
canvas.setAttribute('width', imgObj.width);
canvas.setAttribute('height', imgObj.height);
let context = canvas.getContext('2d');
// 将图片画在画布上
context.drawImage(imgObj, 0, 0);
// 获取像素数据
let imgData = context.getImageData(0, 0, imgObj.width, imgObj.height);
let pixelData = imgData.data;

但这时你去打印pixelData,你会发现结果为:

好家伙,全是 0,,,😳

我一时想不到是什么原因:难道是 canvas 的 api 使用不熟练?

stackoverflow上找到了上面的回答:

但是我修改后还是不行。

这时,我想到图片加载是异步的。可能图片还没加载完毕就开始从画布读取图片数据了,显然这是不对的。于是我对原有代码做了一番调整:

getMainColor("./test.jpeg");
function getMainColor(image) {
  return new Promise((resolve, reject) => {
    try {
      const canvas = document.createElement("canvas");
      const img = new Image(); // 创建img元素
      img.src = image; // 设置图片源地址
      img.onload = () => {
        let color = getImageColor(canvas, img);
        resolve(color);
      };
    } catch (e) {
      reject(e);
    }
  });
}
function getImageColor(canvas, img) {
  const context = canvas.getContext("2d");
  context.drawImage(img, 0, 0);

  // 获取像素数据
  let pixelData = context.getImageData(
    0,
    0,
    canvas.width,
    canvas.height
  ).data;
  console.log("pixelData", pixelData);
  return pixelData;
}

事实证明:it's true

获取了图片数据,下一步就要对其进行相应的处理。

对图片数据进行处理 🦁

展开上一步得到的数据:

这里的数据是什么意思呢?其实就是rgba,分布代表红色(Red)绿色(Green)蓝色(Blue)透明度(Alpha)rgba 的图片每个像素点是由上面四个数值表示的。也就是说每四个为一组。

知道了规律,那让我们来对数据做一下清洗:主要就是对颜色进行分组,并统计每种颜色分别出现的次数:

function getImageColor(canvas, img) {
  const context = canvas.getContext("2d");
  context.drawImage(img, 0, 0);

  // 获取像素数据
  let pixelData = context.getImageData(
    0,
    0,
    canvas.width,
    canvas.height
  ).data;
  console.log("pixelData", pixelData);
  return getCountsArr(pixelData);
}
function getCountsArr(pixelData) {
  let colorList = [];
  let rgba = [];
  let rgbaStr = "";
  // 分组循环
  for (let i = 0; i < pixelData.length; i += 4) {
    rgba[0] = pixelData[i];
    rgba[1] = pixelData[i + 1];
    rgba[2] = pixelData[i + 2];
    rgba[3] = pixelData[i + 3];

    if (rgba.indexOf(undefined) !== -1 || pixelData[i + 3] === 0) {
      continue;
    }
    // console.log("rgba", rgba);
    rgbaStr = rgba.join(",");
    if (rgbaStr in colorList) {
      ++colorList[rgbaStr];
    } else {
      colorList[rgbaStr] = 1;
    }
  }
  console.log("colorList", colorList);

  return colorList;
}

打印colorList结果为:

到这里,我们就得到了每种数据分别出现的次数。

对颜色列表排序 🐳

最后一步,对上面得到的色值对象做一个排序:

for (let prop in colorList) {
  arr.push({
    // 如果只获取rgb,则为`rgb(${prop})`
    color: `rgba(${prop})`,
    count: colorList[prop],
  });
}
// 数组排序
arr.sort((a, b) => {
  return b.count - a.count;
});

console.log("arr", arr);

排序后得到如下结果:

到这里我们就得到了图片色值出现次数从大到小的排序数组,我们来看排在第一位的rgba(206,205,201,255)

再把测试图片贴一下:

肉眼可见的主题色已经被提取出来了!🎉

反思 🚀

最后还是回到文章最开始提到的网易云音乐的播放器特效。不管它的实现方式是怎么样的,它的这种产品创意是值得我们学习的。

我们平时在浏览国内外的一些网站或者使用一些 app 时,总能遇到一些让你拍手称赞的效果。而这些特效往往又与我们前端分不开。

本文分享自微信公众号 - 秋风的笔记(qiufengnote)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-08-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 猫脸辨识没这么容易!GPU加持深度学习才突破瓶颈

    本篇献给奋战在深度学习领域里的铲屎官们! 奇群科技执行长宋牧奇一直想为旗下团队熟悉的先进GPU技术研发实力,找到一个杀手级的应用,经过多次碰壁后,没想到最后却是...

    GPUS Lady
  • 用 上帝视角 来解答你的蛙到底在干什么?(逆向游戏程序逻辑)

    呱呱走火入魔 - 逆向游戏代码 - 终结玄学迷信 看到很多人对物品的使用上的很多猜测,很多都不是很准确。 为了理解你们的呱究竟在干什么,花了五个晚上逆向游戏程序...

    刀刀老高
  • 微软研究院新论文:按语义结构迁移图片视觉属性

    唐旭 编译整理 量子位 出品 | 公众号 QbitAI ? 最近,来自微软亚洲研究院和上海交通大学的Jing Liao、Yuan Yao、 Lu Yuan等人...

    量子位
  • 数据分析告诉你:旅行青蛙的秘密

    最近一款“佛系游戏”《旅行青蛙》(旅かえる)爆红朋友圈,一夜间刮起一股“养蛙(娃)热潮”,知乎一位名叫@黄小秋的程序员大佬,为了让老母亲老父亲们理解自己的呱究竟...

    钱塘数据
  • 一款「 负体验 」的AI产品

    今年我大部分的时间都花在研究人工智能和写书上(文章后面有书的进展,出版社编辑终于可以让我公布出来了哈哈哈),在快要到10月份的时候,我大学毕业时立下的“一年开发...

    mixlab
  • 你的电脑是如何识别色图的?

    在视频监控系统中,计算机甚至能把你能从一大堆东西里给认出来,连你穿啥颜色衣服都能看的一清二楚。

    昱良
  • 深度学习洪流:为何它能瞬间改变你的生活?(上)

    编者按:我们和电脑之间的交流正在发生着转变,而深度学习也已经润物细无声地进入我们的生活,甚至在你意识到这一点之前,世界已经截然不同。 本文首发于fortune,...

    AI科技评论
  • 你的电脑是如何识别色图的??

    在视频监控系统中,计算机甚至能把你能从一大堆东西里给认出来,连你穿啥颜色衣服都能看的一清二楚。

    小林C语言
  • 18个最佳的产品页面设计(下)

    引言:本文展示了如何让页面变得有趣个性化,展现更多细节和与众不同,让访问者轻松获得想要的信息,下面的18个产品页面设计的最佳案例不容错过。

    iCDO互联网数据官
  • 每周分享第 24 期

    以前的 3D 打印,一般都使用塑料。今年,3D 金属打印机问世了,可以用金属打印零件,生成更轻、更坚固、更复杂的形状,而且成本更低、速度更快。这为复杂的金属模具...

    ruanyf
  • 白话理解各种术语(持续更新...)

    场景1:打电话 A:喂,听得到吗?(发送连接消息) B:喂,听得到,很清楚,你听的清楚吗?(接受消息,并发送一个询问接受的包给客户端) A:听的清楚(客户...

    LittleU
  • 这种场景你还写ifelse你跟孩子坐一桌去吧

    if else,并不是一个非常坏的关键字,只不过有人把他用坏了。尤其在接到产品需求如下这样;日期需求紧急程度程序员(话外音)星期一.早上猿哥哥,老板说要搞一下营...

    小傅哥
  • AI换脸、虚拟主播、智能影像生产……文娱产业现在已成AI的天下了

    有网友将最近播出的电视剧《都挺好》中让人恨得牙痒痒的苏大强,用AI换脸变成了眉清目秀的吴彦祖,场景竟毫无违和感。

    量子位
  • 少年你骨骼精奇?AI现在可以帮你看一看了

    唐木 发自 浙江杭州 量子位 报道 | 公众号 QbitAI ? “少年,我看你骨骼精奇,是个练武奇才,这里有一本《如来神掌》……” 现在,AI就能判断你是...

    量子位
  • ACM TOMM 2017最佳论文:让AI接手繁杂专业的图文排版设计工作

    编者按:你是否曾经为如何创作和编辑一篇图文并茂、排版精美的文章而烦恼?或是为缺乏艺术灵感和设计思路而痛苦?AI技术能否在艺术设计中帮助到我们?今天我们为大家介绍...

    马上科普尚尚
  • 这种场景你还写ifelse你跟孩子坐一桌去吧

    if else,并不是一个非常坏的关键字,只不过有人把他用坏了。尤其在接到产品需求如下这样;

    小傅哥
  • 硬纪元干货 | 爱奇艺吴霜:看好互动视频、AI陪伴以及VR直播

    镁客网
  • 怎样用GAN生成各种胖吉猫?谷歌大脑程序员教你撩妹神技

    Facebook聊天框里出道的灰色短毛猫Pusheen,是柔软的微胖界宠儿,中文名字叫胖吉。

    量子位
  • 手把手教你用PyTorch实现图像分类器(第二部分)

    回想一下,在本系列文章的第一篇中,我们学习了为什么需要载入预训练网络以及如何载入预训练网络,同时我们演示了如何将预训练网络的分类器替换为我们自己的分类器。在本篇...

    AI研习社

扫码关注云+社区

领取腾讯云代金券