学习
实践
活动
专区
工具
TVP
写文章
专栏首页腾讯云AI最佳实践|用腾讯云AI图像搜索打造属于自己的拍立淘
原创

最佳实践|用腾讯云AI图像搜索打造属于自己的拍立淘

最近,在一个论坛交流会上, 有嘉宾提出自己运营多年的微信小程序商城经常收到用户反馈:自己在逛街时候发现别人穿的好看的衣服,很难通过关键字定位到具体的商品,如果能拍照定位相关的商品就好了,问目前小程序里面能否实现这样的功能。作为一名软件开发者, 日常网购也有类似的体会。如果能在小程序里集成商品搜索的功能,就能大大提升用户的体验,嘉宾的问题引发我极大的兴趣。

在调研过程中,发现腾讯云图像分析的图像搜索产品可以基于输入图片,智能识别图片中的商品主体,在自建图片库中搜索相同或相似的商品图片,并给出相似度打分。如果输入检索的图片包含服饰类商品,可智能识别上衣、下装、裙装、鞋、包、配饰等多种服饰的类别、颜色以及其他特征属性,实现电商场景下的以图搜图。

接下来 ,将详细分享一下我是如何在小程序里实现商品搜索的。

一、准备工作

1.1明确目标

在小程序里,通过输入商品图片来定位相似的商品图,类似于下面这个:

1.2了解图像搜索

在开始使用之前,还是得对我即将要用的产品进行一个比较详细的了解。

官网文档介绍

接口文档

1.3开通图像分析服务

接下来就可以按照官网文档的指引在腾讯云官网开通图像分析服务:

开通服务后 ,创建图片和检索图片分别会发放一万次免费资源包, 可以在资源包管理页面查看使用情况:

二、开发流程

2.1获取个人密钥

在腾讯云官网访问管理页面, 新建个人密钥:

2.2在线调试

下面通过在线调试的方式简单的实现拍立淘的功能。

(1)图库类型选择

首先查看图像搜索的文档,我们选择商品图像搜索的服务类型。

(2)创建图库

创建图片库,指定商品图像搜索, 腾讯云官网提供了在线调用 API Explorer 工具,方便我们可视化调用。

(3)图片入库

创建图片, 将商品图片入库到指定图库中,返回值中包含了主体位置信息。

(4)商品搜索

检索图片, 指定商品图,在图库中进行检索 ,获取相似或者相同的商品图, 返回结果中包含了与输入图类似的商品ID。

这样就基本上演示出了商品搜索的过程,对于使用而言,可以对输入的参数进行一些微调,满足更多的要求。

2.3使用SDK调用

(1)文档介绍

正式接入的话,需要集成腾讯云官网上提供的SDK。 在文档的最下方,提供了多个语言的SDK,可以根据自己熟悉的语言进行接入。

(2)示例

以go语言为例,演示下如何使用。

第一步: 安装基础包:

go get -v -u github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common

安装图像分析依赖

go get -v -u github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tiia

第二步: 实现调用,以创建图库为例。

credential := common.NewCredential(***, ***)
clientProfile := profile.NewClientProfile()
clientProfile.HttpProfile.Endpoint = "tiia.tencentcloudapi.com"

cli, err := tiia.NewClient(credential, regions.Guangzhou, clientProfile)
if err != nil {
	panic(err)
}
//实例化一个请求结构
req := tiia.NewCreateGroupRequest()
//对请求参数进行赋值
req.GroupId = common.StringPtr("groupId")
req.GroupName = common.StringPtr("groupName")
req.Brief = common.StringPtr("brief")
req.MaxCapacity = common.Uint64Ptr(uint64(100))
req.MaxQps = common.Uint64Ptr(uint64(10))
req.GroupType = common.Uint64Ptr(uint64(5))
//调用请求
resp, err := cli.CreateGroup(req)
if err != nil {
	return
}

三、小程序实现商品搜索

上面介绍了图像搜索的基本能力,如何应用商品搜索能力,来实现拍立淘的效果呢,接下来以小程序为例,来演示一个简单的应用:

3.1构建底库

根据上述文档, 我们在服务端使用sdk来初始化图片库,然后通过小程序端来访问,这里不再赘述。

1. 使用2.1申请的secretID,secretkey来创建图库。

2. 商品图入库。

3.2构建小程序

index.js

const app = getApp()

Page({
  data: {
    urls: [],
    inputValue: "图片URL",
    buttonStatus: false,
    category: ['Jacket', 'dress', 'trousers', 'bag', 'shoe', 'accessories'],
    motto: 'Hello World',
    userInfo: {},
    hasUserInfo: false,
    canIUse: wx.canIUse('button.open-type.getUserInfo'),
    canIUseGetUserProfile: false,
    canIUseOpenData: wx.canIUse('open-data.type.userAvatarUrl') && wx.canIUse('open-data.type.userNickName') // 如需尝试获取用户信息可改为false
  },
  // 事件处理函数
  bindViewTap() {
    wx.navigateTo({
      url: '../logs/logs'
    })
  },
  onLoad() {
    if (wx.getUserProfile) {
      this.setData({
        canIUseGetUserProfile: true
      })
    }
  },
 
  getUserProfile(e) { 
    // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认,开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
    wx.getUserProfile({
      desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
      success: (res) => {
        console.log(res)
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        })
      }
    })
  },
  getUserInfo(e) {
    // 不推荐使用getUserInfo获取用户信息,预计自2021年4月13日起,getUserInfo将不再弹出弹窗,并直接返回匿名的用户个人信息
    console.log(e)
    this.setData({
      userInfo: e.detail.userInfo,
      hasUserInfo: true
    })
  },

  isUrl (url) {
    var urlRegExp=/^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\*\+,;=.]+$/;
    if(urlRegExp.test(url)){
    return true;
        }else{
    return false;
        }
  },

  imageSearch() {
    var that = this
    wx.request({
      url: 'http://****/search',
      data: {
        "GroupId": "***",
        "ImageUrl": this.data.inputValue
      },
      method: "POST",
      header: {
        'Content-Type': "application/json"
      },
      success (res) {
        if (res.data == null) {
          wx.showToast({
            icon: "error",
            title: '请求失败',
          })
          return
        }
        console.log(res.data)
        that.setData({
          urls: res.data.Urls,
          object: res.data.Object
        })
        that.drawInputImage()
      },
      fail(res) {
        wx.showToast({
          icon: "error",
          title: '请求失败',
        })
      }
    })
  },

  drawInputImage: function() {
    var that = this;
    wx.downloadFile({
      url: that.data.inputValue,
      success: function(res) {
        var imagePath = res.tempFilePath
        // 绘制
        wx.getImageInfo({
          src: imagePath,
          success: function(res) {
            wx.createSelectorQuery()
            .select('#input_canvas') // 在 WXML 中填入的 id
            .fields({ node: true, size: true })
            .exec((r) => {
              // Canvas 对象
              const canvas = r[0].node
              // 渲染上下文
              const ctx = canvas.getContext('2d')
              // Canvas 画布的实际绘制宽高 
              const width = r[0].width
              const height = r[0].height

              // 初始化画布大小
              const dpr = wx.getWindowInfo().pixelRatio
              canvas.width = width * dpr
              canvas.height = height * dpr
              ctx.scale(dpr, dpr)
              // 清空画布
              ctx.clearRect(0, 0, width, height)

              let radio = height / res.height
              console.log("radio:", radio)
              const img = canvas.createImage()
              var x = width / 2 - (res.width * radio / 2)

              img.src = imagePath
              img.onload = function() {
                ctx.drawImage(img, x, 0, res.width * radio, res.height * radio)
                var allBox = that.data.object.AllBox
                ctx.fillStyle = 'rgba(247, 15, 2, 0.7)';
                for (var i in allBox) {
                  if (allBox[i].Rect.X == that.data.object.Box.Rect.X && 
                    allBox[i].Rect.Y == that.data.object.Box.Rect.Y) {
                      continue
                  }
                  ctx.fillRect(x + (allBox[i].Rect.X * radio), 
                    allBox[i].Rect.Y * radio, 
                    allBox[i].Rect.Width * radio,
                    allBox[i].Rect.Height * radio);
                }
                // 绘制主体
                ctx.fillStyle = 'rgba(159, 255, 125, 0.7)'; //rgba(0, 0, 200, 0.5)
                ctx.fillRect(x + (that.data.object.Box.Rect.X * radio), 
                  that.data.object.Box.Rect.Y * radio, 
                  that.data.object.Box.Rect.Width * radio,
                  that.data.object.Box.Rect.Height * radio);
                  console.log(that.data.object.AllBox)

                // 绘制文字背景
                let text = `${that.data.category[that.data.object.CategoryId]} ${that.data.object.Box.Score}`
                ctx.fillStyle = '#221329'
                ctx.fillRect(x + (that.data.object.Box.Rect.X * radio), that.data.object.Box.Rect.Y * radio, that.data.object.Box.Rect.Width * radio, 15)
                // 绘制文字
                ctx.fillStyle = '#fcfafc'
                console.log(that.data.category[that.data.object.CategoryId])
                ctx.fillText(text, x + (that.data.object.Box.Rect.X * radio), that.data.object.Box.Rect.Y * radio + 10) 
              }          
            })
          }
        })
      }
    })
  },

  handlerInput(e) {
    console.log(e)
    this.setData({
      inputValue: e.detail.value
    })
  },

  handlerSearch(e) {
    console.log(this.data.inputValue)
    if (!this.isUrl(this.data.inputValue)) {
      console.log("error url: ", this.data.inputValue)
      wx.showToast({
        icon: "error",
        title: '请输入正确url',
      })
      return 
    }
    this.setData({buttonStatus: true})
    this.imageSearch()
    this.setData({buttonStatus: false})
  },
  handlerInputImage(e) {
    console.log(e)
  }
})

index.wxss:

/**index.wxss**/
.userinfo {
  display: flex;
  flex-direction: column;
  align-items: center;
  color: #aaa;
}

.userinfo-avatar {
  overflow: hidden;
  width: 128rpx;
  height: 128rpx;
  margin: 20rpx;
  border-radius: 50%;
}

.usermotto {
  margin-top: 200px;
}

page {
  background: rgb(255, 255, 255);
}
.page-section-spacing {
  width: 100%;
}
.container {
  padding: 0;
}

.button_container {
  margin-top: 600rpx;
}
.flex-wrp{
  margin-top: 60rpx;
  margin: auto;
  width: 90%;
  overflow: hidden;
}

.form-item {
  margin-bottom: 10px;
  margin-top: 10px;
  width: 90%;
  overflow: hidden;
}
.flex-item{
  float: left;
  width: 300rpx;
  height: 400rpx;
  font-size: 26rpx;
  margin: 10rpx 10rpx 0rpx 20rpx;
  overflow: hidden;
}
.flex-item-V{
  margin: 0 auto;
  width: 300rpx;
  height: 200rpx;
}


.input {
  width: 75%;
  float: left;
  height: 36px;
  border: 1px solid #ddd;
}

.button {
  float: right;
  height: 36px;    
  background: rgb(236, 111, 61);
  color: white;
}

.view_line {
  width: 96%;
  height: 50rpx;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: left;
  margin: 0rpx 2% 0rpx 2%;
}

.view_line view {
  width: 75%;
  height: 2rpx;
  background: linear-gradient(to right,#706f70, #706f70);
}

.text_line {
  font-size: 25rpx;
  color: rgb(66, 66, 66);
  margin: 0rpx 2% 0rpx 2%;
}
.image {
  width: 100%;
}
.image image {
  width: 100%;
  background-size: auto 100%;
  background-repeat: no-repeat;
}

index.wxml:

<view class="container">
   <div class="form-item">
    <input placeholder=" 商品URL" class="input" bindinput="handlerInput" />
    <button class="button"  loading="{{buttonStatus}}" bindtap="handlerSearch" size="mini">搜索 </button>
   </div>
   <view class="view_line">
    <text class="text_line">输入图片</text>
    <view></view>
   </view>
   <canvas
      type="2d"
      id="input_canvas"
      style="background:rgb(228, 228, 225); width: 360px; height: 240px;"
    ></canvas>
   <view class="view_line">
    <text class="text_line">搜索结果</text>
    <view></view>
   </view>
  
   <view class="page-section-spacing">
        <view  class="flex-wrp" style="flex-direction:row;">
          <view wx:for="{{urls}}" class="flex-item">
          <view class="image"> 
            <image mode="aspectFit" style="background-color: #eeeeee;" src="{{item}}"></image>
            </view>
          </view>
        </view> 
      </view>
  </view>

编译即可:

3.3构建运行

输入商品图,图搜服务会进行主体识别,然后再进行相似图搜索,可以看到效果如下:

如果不做主体识别,可能搜不到我们想要的结果,例如,我们想要这个墨镜,但是不开主体识别的话可能会定位到衣服,导致没有结果:

看下图搜主体识别的文档介绍:

118px使用商品图像搜索默认会开启主体识别,涉及到以下两个参数:

默认情况下主体识别是打开的,服务会针对输入图中的服饰进行主体位置识别,以实现更好的搜索效果,如下:

按照上述主体识别的描述,指定下配饰类目,服务会定位到墨镜的位置,就可以搜索到预期的商品了:

到这一步就实现了小程序中商品搜索的基本功能,涉及到商品搜索的场景,都可以参考下。后面给朋友展示了小程序demo, 体验下来,使用效果相当满意。

3.4查看调用量

在后续观察中,可以在腾讯云官网, 进入到图像分析控制台,可以查看最近的调用情况。

了解更多图像搜索功能:https://cloud.tencent.com/product/imagesearch

原创声明,本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

登录 后参与评论
0 条评论

相关文章

  • 最佳实践!用腾讯云AI语音合成打造自己的第一本有声书

    现代生活中,我们不可避免会遇到很多碎片时间,等公交、倒地铁、排核酸、买早点等等。这些时间累积起来,无疑是一笔很大的个人资源,而想利用这部分时间,听显然是最好的方...

    腾讯云AI
  • 大厂技术实现 | 图像检索及其在淘宝的应用 @计算机视觉系列

    图像检索任务指的是,给定查询图像,从图像数据库中找到包含相同或相似实例的图像。典型应用之一就是电商商品检索,如淘宝拍立淘,只需要用户随手拍照即可精准检索,提高了...

    ShowMeAI
  • 人工智能的技术变革:HMS Core让你也拥有《星球大战》中的机器人 | Q推荐

    你还记得《星球大战》电影中的礼仪机器人 C-3PO 吗?就是那个承担着不同种族、不同物种间翻译交流作用的礼仪机器人。C-3PO 通过从数据库中下载资料来更新自己...

    深度学习与Python
  • 大厂留不住技术大牛了?阿里金榕、京东于建强、字节梅晓等纷纷离职

    ---- 新智元报道   编辑:编辑部 【新智元导读】在这个互联网打工人纷纷「毕业」的年景,阿里、京东、字节的AI负责人相继被曝离职。 5月19日,据Te...

    新智元
  • 阿里深度学习实践

    近年来,随着大数据在互联网的蓬勃发展,很多人工智能的技术、应用像雨后春笋般涌现出来,如谷歌、Facebook、阿里、腾讯、百度等用得非常广泛,且各种应用都通过...

    机器学习AI算法工程
  • 阿里深度学习实践

    用户1737318
  • 直播预告 | 社交文娱赛道如何卷赢对手?

    移动互联网增长见顶已是一个公认的事实。 中国互联网络信息中心 (CNNIC)发布的《中国互联网络发展状况统计报告》显示,截至2021年12月,中国手机网民用户规...

    腾讯云AI
  • 腾讯吴运声:人工智能与行业应用正展开一场“双向奔赴”的范式变革

    9月3日,2022年世界人工智能大会“腾讯论坛”在上海举办。腾讯云副总裁、腾讯优图实验室总经理吴运声发表了“加快AI技术与场景创新,助力数字经济高质量发展”主题...

    腾讯云TI平台
  • 产品分享 | 如何用AI打造爆款互动玩法

    随着科技的快速发展,AI技术为各行各业注入新的生命力,成为公众日常使用的技术之一。尤其是在泛娱乐领域,AI为视频编辑、互动营销、视频直播等场景赋予了更多样、更有...

    腾讯云TI平台
  • 【资料合集】2019 云+技术沙龙盘点回顾

    腾讯云开发者社区技术沙龙
  • 腾讯云AI牵头制定2项计算机视觉标准

    6月30日,中国电子工业标准化技术协会正式发布《人工智能 深度合成图像系统技术规范》《人工智能 智能字符识别技术规范》《人工智能 视频图像审核系统技术规范》等3...

    腾讯云AI
  • 腾讯吴运声:人工智能与行业应用正展开一场“双向奔赴”的范式变革

    9月3日,2022年世界人工智能大会“腾讯论坛”在上海举办。腾讯云副总裁、腾讯优图实验室总经理吴运声发表了“加快AI技术与场景创新,助力数字经济高质量发展”主题...

    腾讯云AI
  • 再获权威认证!腾讯云智能入选《IDC MarketScape:2022全球通用计算机视觉厂商评估》

    近日,全球领先的IT市场研究和咨询公司IDC发布了2022年度《MarketScape:全球通用计算机视觉厂商评估》报告(以下简称“报告”),凭借腾讯云智能在计...

    腾讯云TI平台
  • CVPR 2017国内外亮点论文汇集:史上最盛大会议,华人占据半壁江山

    机器之心原创 参与:李亚洲、路雪、李泽南 深度学习界的「春晚」CVPR 2017 已在夏威夷火奴鲁鲁 Hawaii Convention Center 开幕,在...

    机器之心
  • 精选腾讯技术干货200+篇,云加社区全年沙龙PPT免费下载!

    “看一看”推荐模型揭秘!微信团队提出实时Look-alike算法,解决推荐系统多样性问题;

    风间琉璃
  • 别再追捧AI概念,来看腾讯AI商业化实践

    导语 | AI的价值,要从实践中追寻。从1956年达特茅斯会议上的学术概念,到今天在人脸识别、智能客服、智能家居、医疗诊疗、工业机器人、无人驾驶等领域的多点开花...

    TVP官方团队
  • Get不到AI的点?一定要看《程序员的AI书:从代码开始》!

    当老姑大伯们渐渐把AI和程序员画上等号时,我大腿一拍大事不妙!生怕疫情后的家庭聚会上,让我表演才艺:做个什么狗陪他们下棋、做个什么精灵跟他们唠嗑……

    博文视点Broadview
  • 贾佳亚离开腾讯优图实验室,创立AI公司思谋科技获 Pre-A 轮融资

    2 月 27 日晚间机器之心消息,原腾讯杰出科学家、优图实验室 X-Lab 负责人贾佳亚离职创立了一家新的 AI 公司——思谋科技 (SmartMore)。贾佳...

    机器之心
  • 中国AI半壁江山:微软亚洲研究院20年20大创业公司

    这20年里,发表论文5000多篇,和300多所大学合作,设立了180多个博士项目,院友人数超过7000名。

    量子位

扫码关注腾讯云开发者

领取腾讯云代金券