Skip to content

chAwater/SkierFinder

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SkierFinder

2021年12月30日更新,所有功能滑呗APP均已实现,项目仅供学习使用,侵删。

源起

2019年3月31日,和 @MingxuanHu 一起在崇礼万龙滑雪场滑雪,他第一次使用“滑呗”APP记录滑雪数据。下午坐大巴车返京途中,我们看到有同行的雪友在微信分享自己的滑雪照片。

这些滑雪照片来自于“滑呗”APP的“照片墙”页面,实际上是摄影爱好者或雪场摄影师将自己的作品上传到“滑呗”APP的。广大雪友根据自己的所在雪场是否观察到有摄影师拍摄自己的滑行、被拍摄的大概时间,来找到自己的照片(当然,也可以下载其他雪友照片和风景照片,不在本文讨论范围)。“滑呗”APP提供的照片带有大量水印,若想下载无水印、高清、超清照片,需支付一定(约5元、15元、25元)的费用,“滑呗”APP和摄影师 3:7 分成。由于雪友属于社交性质较强的的社群,且消费水平普遍较高,这个产品不仅可以产生广告效应进而扩大用户群,也可以为“滑呗”APP带来一定的收益,还能创造其他潜在的商业价值(雪场广告效应、雪场流量数据、用户浏览数据等)。

但是,雪友的滑雪水平理论上符合正态分布,意味着多数雪友并不具备高超的滑雪水平。因此,在摄影师经常出没的高级道(如崇礼万龙滑雪场的大奔头)上,虽然能够诞生很好的摄影作品,但通常被摄目标有限fig,进而导致摄影师的收入有限。摄影师显然也注意到这一点,因此他们通常还会去其他相对简单的中级雪道缆车沿线、山顶或山脚集合地等位置进行拍摄fig。但由于雪友水平限制(注意力高度集中在滑行上)或这些位置的特点(摄影师隐蔽、人员嘈杂等),导致雪友很难观察到有摄影师拍摄自己。最终,多数有意愿获得自己照片的雪友将不得不从众多照片中手动找寻自己。以2019年3月31日(周日)为例,崇礼万龙滑雪场就产生了六千多张照片,我们有理由认为,从“照片墙”上大量照片中找到自己的过程非常困难,是此产品成交率低的重要原因之一。因此,如何高效找到特定雪友的照片,是提高雪友付费概率的重要环节。

思路

如何高效找到特定雪友的照片,这个问题可以被分解为以下几步:

  1. 在大量照片中找出每张照片中的每个滑雪者;
  2. 提取每个滑雪者的特征,建立数据库;
  3. 用户上传一张自己的照片,提取特征后与数据库进行匹配;
  4. 返回匹配度高的照片给滑雪者挑选。

我们的想法是利用最先进的(State-of-art)、基于人工智能的图像识别技术,帮助我们高效、准确的完成这个“寻找 -> 提取 -> 匹配”的过程。

(:beginner: 在我当前微薄的知识储备下认为)其中可能涉及到的技术、人工智能算法、模型和结果主要有:

  1. 可以通过爬虫等方式,获取“滑呗”APP上一定量的低清晰度照片
    • 爬虫
      • 发现一个相关 repo(已克隆)
    • 其他
      • 抓包 by @MingxuanHu
        • Update: 2019.04.02 抓包失败了
  2. 在大量照片中找出每张照片中的每个滑雪者
    • 物体识别(Object Detection)和 图像分割(Image Segmentation)
      • 模型:Mask R-CNNYOLO;数据库:COCO
      • 优点:模型高度成熟;缺点:不太熟悉,需要测试;
  3. 提取每个滑雪者的特征,建立数据库
    • 特征工程
      • 不同颜色像素的比例
      • 优点:非常简单; 缺点:不准确;
    • 卷积神经网络提取特征
      • 区分单板/双板;区分男女
      • 优点:简单;缺点:依赖人工标注,工作量大,功能有限;
    • 非监督学习、降维、聚类
      • PCA & tSNE & Autoencoder
      • 优点:简单;缺点:效率低,每次新加入数据,就需要重新跑一遍;
    • 姿态识别(Pose Estimation)
      • 根据姿势区分雪服/雪裤;区分单板/双板
      • 优点:模型比较成熟,提取额外特征;缺点:完全不了解,需要学习;
  4. 用户上传一张自己的照片,提取特征后与数据库进行匹配、索引
    • 与上一步类似,但有区别
      • 摄影角度差别很大,信息完整度有差别(相比于滑行中,头、脸、腿信息可能不完整)
  5. 返回匹配度高的照片给滑雪者挑选
    • 容忍假阳性;抗拒假阴性;
    • 处理非被摄主题匹配照片的方式很棘手
  6. 结果
    • 成功跑通流程,成立公司与“滑呗”接触,提高准确度,投入商业化,赚钱(白日梦 😍 )
    • 边学边玩、纯玩、烂尾(这个靠谱 😎 )

上手

TODO

数据获取

  • 爬虫
    • 如何获得 API ⚠️
  • 其他渠道获取照片 💤

特征提取

特征分析

图像索引

  • 构建数据库
  • 构建用户上传图片
  • 基于图像变换(Argument)检测索引成功率
  • 基于大量图片的索引检测
    • 在多个时间点寻找同一个滑雪者
    • 寻找类似雪服的滑雪者

NEXT

  • 尝试布置计划到看板
  • 下一步计划中......

TAG

TAG Name TAG emoji
HEAD-TODO 📌
CHECKPOINT
FOLK ⤵️ ↩️ 🔀
FINE-TUNING 🚧 🎵
QUESTION
LOOSE-END ➰ 🔚

获取滑雪照片

Folder: from_fenxuekeji

  • 利用找到的API尝试get照片 01.Test_API_get_img.py
  • 获取一定量的照片URL 02.Scraping_urls.py
  • 下载照片 download_urls.sh
  • 其他 ⚠️
    • 担心 API 发生变化,影响与公司合作前的时间窗口 (不能过分依靠好运气得到的API 😎 )
    • 上家技术还需要提高 😟
      • @MingxuanHu 写的 Java 脚本暴力爬,搞了几个G竟然没被ban
      • 同一张照片有各种大小
        • 应该是为了APP的小图预览(我下载了大照片)
        • 有不同大小的照片,因此可以用AI增加分辨率
        • 没有关注原始图像能否直接获取 👿
      • 小照片没水印,大照片有水印
        • 所有照片水印都是一样的,因此可以用AI去掉 😈
    • 不过既然吃了上家资源又要赚上家钱,就不坑上家了 😬

在照片中找出每个滑雪者

  • 配置 Mask R-CNN 运行环境 setup_MaskRCNN.sh
    • imgaug, pycocotools 以外,其他为常用包 (:beginner: 来自于不做CV的无知 :mask: )
  • 测试 Mask R-CNN demo.ipynb
  • 在单反相机照片和用API手动get的照片上测试 Mask R-CNN ( based on demo.ipynb )
    • 效果比想象中的好!:v:
    • 有些照片模型能够识别出雪板!单板双板直接可以区分! :ski:skis /:snowboarder:snowboard
ST_01 (相机照片,模型未检测到雪板) ST_02 (相机照片,模型检测到雪板)

提取每个滑雪者的特征

Folder: analysis

从 Mask R-CNN 的模型输出提取信息

01.Mask_RCNN_result_analysis.py

  1. 保存每个照片每个对象的 box, mask, class, scores 为 DataFrame
  2. 初步分析所有图片的结果
    • Fig1 Score 分布
      • 整体:0.7~1.0
      • 人:~1.0
      • 雪板:0.7~1.0
    • Fig2 每个照片的 Box 数量分布
      • RawData: Median=3
      • CleanData: Median=2
      • 对象多处在图片正中央
        • 摄影中的黄金分割不见了
        • 可能和运动摄影的速度要求有关 (焦点在中央以减少“对焦到按下快门”时间 😎 )
    • Fig3 Class 数量分布
      • 主要对象为人和雪板
      • 其他类别的鉴定可认为是“错误”忽略
    • Fig4 BoxSize 分布:见图
  3. 保留有意义的信息 CleanData
    • Class in ['person', 'skis', 'snowboard']
    • Score > 0.7
    • Person's Score > 0.9
    • Person's BoxSize > 1% ImageSize
      • 删掉非被摄主体
      • 平均 ~3人/照片 (“抢镜头”的人很多很多,还是应该说这个模型真:ox::beer:)
    • 使用上面的参数删掉了 ~40% 的 Box
    • CleanData保存成 .pkl 文件大小约 3G,保留 ~10k 个 Box

⚓ ⚓ ⚓ ⚓ ⚓ ⚓ ⚓ ⚓ ⚓ ⚓ ⚓ ⚓ ⚓ ⚓ ⚓ ⚓ ⚓ ⚓ ⚓ ⚓ ⚓

用提取的信息构建数据库

02.Extract_features_from_InBoxPixels.py

  1. 从原始图像中提取出 InBox Pixels
  2. 固定提取出的 BoxSize
    • 函数 squareBox
    • 等比缩放,填充白色
    • shape = (150, 150, 3)
  3. 卷积神经网络进一步提取 InBox Pixels 的特征 ⤵️
    • 函数 extInBoxPixels
    • 模型:VGG19 ResNet50 DenseNet201
    • 每个滑雪者转换为一个多维向量,保存
    • 聚类时是否要做 Normalization ❓
    • References
  4. 特征分析 💤
    • 对 DenseNet201 提取出的特征向量降维、聚类
      • 使用三种算法降维(PCA, tSNE, UMAP)低维展示
      • 手动选择一簇低维空间中的点,可视化其 InBox Pixels 和原始图片
        • 选择了 UMAP 二维空间中最上面中间偏右的那一簇数据点
          • 结果很好,目标几乎是同一个人
          • ResNet50 的结果有一个不是,可能是那一簇旁边的那个点
      • 使用 HDBSCAN 聚类 低维展示
        • 以 tSNE 结果为例
        • 参数需要微调,结果不稳定,不理想 🚧
          • 有的聚类项目太多无意义
          • 有的聚类不是由滑雪者雪服聚出来的,而是动作+雪花
          • 有的聚类包含两个类似的滑雪者
          • 有的多个聚类属于同一个滑雪者
      • 需要进一步学习提高 ➰ 🔚
  5. 构建数据库
    • 使用特征向量的余弦相似度(点乘)或欧氏距离表示两个滑雪者特征的相似程度
    • 效果不理想,感觉找出相似的滑雪者不完全是根据雪服(颜色),而是根据动作/姿态
    • 使用 InBox 或 InMask、三种网络提取特征、是或否标准化,都没有很好的效果
    • 需要进一步学习提高 ➰ ↩️
  6. 卷积神经网络区分单板双板 ⤵️

📌

  • TODO outline #TAG-FOLK
    • 数据库设计
      • 设计数据库结构
      • 如何验证?
      • 如何快速可视化结果?
    • 卷积神经网络
      • 利用 Mask R-CNN 输出作为标记,训练 CNN 区分单板/双板
    • 姿态识别
    • etc.

根据上传的照片进行匹配

  • 在上传的照片上运行特征提取
    • Mask R-CNN
    • DenseNet201
  • 在数据库中搜索类似的滑雪者
    • 用向量点乘(余弦相似度)来搜索相近的向量
    • 根据相似度排序数据库中的图片
    • 返回图片排序

📌

结果评估


细节

DataFrame

class scores x1 y1 x2 y1 box_size masks imgID
0 person 0.999791 244 614 400 684 3.340471 000000... ./path/to/imgs/0.jpg
1 person 0.999780 211 299 344 432 5.411135 000000... ./path/to/imgs/0.jpg
2 snowboard 0.991058 320 268 365 417 2.051086 000000... ./path/to/imgs/0.jpg
3 skis 0.944588 384 622 404 686 0.391557 000000... ./path/to/imgs/1.jpg
4 person 0.900126 221 138 249 156 0.154176 000000... ./path/to/imgs/1.jpg

初步分析

降维展示

Version
Obj Version Note
滑呗 APP v3.4.1 iOS
Mask R-CNN v2.1 -
Python 3.7 3.5
Pandas v0.24 v0.23
Keras v2.2.4 v2.1.3
CUDA v10.1 v8.0