How do human sketch objects?

本文介绍了一种基于梯度方向构建草图特征向量的方法,并详细阐述了特征提取、词汇构建、特征量化及分类预测的过程。通过实验对比多种支持向量机(SVM)分类策略,最终实现了草图的有效分类。

文章的实现,OpenCV2.3.1,VS2010。

题目为“人类怎样画草图”,文章前面论述了一大堆关于人类对草图的识别,在这里略去,有兴趣可以自行阅读。

 

在5 Sketch Representation中开始介绍了如何为sketch构造出合适的histogram:主体的思路是利用梯度信息来构造特征向量,在这里只用到梯度的方向,而忽略梯度大小。

5.1 Extracting local features

我们下下来得dataset的图片分辨率过大,首先采用降采样为256*256。

第一步,求得图片的梯度(利用SCHARR算子),这样之后每一个像素点都有一个梯度方向在0~180度之间,然后我们根据梯度方向构造出四个response images,也就是四个bins(O1,O2,O3,O4,分别代表0-45,45-90,90-135,135-180度),然后根据每个像素点梯度的方向处在的区间放入不同的bin中(可以把response images当作二值图像,也就是一个像素点,只能在一个bin中相应像素点的值为1,而其他三个bins相应点为0,当然也出现了梯度的xy方向都为0,在这里我们不将该点放入bins中)。

第二步,将降采样后的图片确定28*28=784个采样点(每个采样点之间差8个像素,第一个点和左边缘相差16个像素,这样的话就舍弃了最后8个像素没有patch,当初没考虑好),以这784个采样点为中心构建784个patches,每个patch为32*32大小。

第三步,每个patch按空间分为4*4=16个bins,也就是每个bins是8*8的块。接下来就是extract 每个块中的每个response images(O)的局部描述子Li。对于每个response images,计算所属的8*8块内的累计值,也就是把第一步中的为1的点累加起来,然后把patch中的16个块的累计值合起来就形成了一个16维的局部描述子Li。然后对于每个response images计算描述子,然后将四个局部描述子合起来形成了该patch的向量d=[L1,L2,L3,L4],为64维向量,在这里也就是patch的特征,之后再对其进行归一化处理(这里一定要归一化,不然在下文的一个参数选取上会造成很大影响)。(文中提到了用能量累积的方法得到d,但是没有采用这个方法实现)

5.2 Building a visual vocabulary

第四步,如上步所做,一幅图就有784个特征向量,于是很多图的所有的特征向量就构成了所谓的bag-of-features,D={di}。在这里我们对选取了前1,000,000(由于考虑到概率问题,个人认为前100000个向量和随机选取1000000个向量在概率上是相等的,所以就没有采用文中所说的随机选取)个特征向量进行k-means聚类,取k=500,聚类完成之后得到了500个中心向量{ui},这里也可以认为是words。(关于bag-of-features或bag-of-words的问题可以看http://blog.youkuaiyun.com/tinyway/article/details/9240821)

5.3 Quantizing features

第五步,这一步中,我们就要构建出属于一个sketch的histogram。首先还是计算出784个d,之后将每个d与500个ui进行高斯核的计算距离K(d,u),形成了一个500维的距离向量q(d)=[K(d,u1),K(d,u2)....K(d,u500)],然后再将q(d)进行归一化(这里的归一化使用绝对值和来归一化,同第三步里d的归一化不同)。在计算完784个q(di)之后,将所有di累加起来,再除以784,就形成了sketch的histogram。



在这里,关于sketch的特征提取就差不多完成了,接下里就考虑分类问题。

问中还提到了很多分类问题,在这里就利用svm进行分类预测,下文讲的是自己的摸索过程,毕竟从没用过svm,大神绕道。

我暂时只取了25类进行分类训练,第一次是进行多分类训练,也就是只训练出一个模型,之后发现结果超级不行,再重看文章后发现文章不是这样实现的,在7.1中提到了,为每个sketch训练了250个分类器,用于将该sketch和其他类的sketch辨别出来,在预测的时候,通过比较每个分类器的classifier function来确定输入样本所属的类别。

同时,由于高斯核距离的计算中,选择了不同的sigma,有不同的效果,当sigma小的时候,sketch的histogram有很多维为0,但是当sigma取大的时候,预测的结果很差,所以还是取小的sigma。

svm的训练方法:

1.直接训练出多分类器:

该方法很直接,但是准确率也不是很高。

2.训练25个二分类器(样本有25类):

同文中7.1所说的方法进行预测,结果同样很不准。每个类取了60个正样本,而其他24个类取了60*24个负样本,这样子训练之后发现classifier function完全一样,看来是正负样本的比例失常导致,故重新训练,正样本取60个,每个类取2个负样本,一共48个负样本,结果也不怎么好,因为有一个分类器的classifier function总是特别大,于是很容易选到那个类别去。(后来改成10类)

3.训练10*9/2个分类器(共10类,两两一个分类器)

一轮轮判断,类似于二叉树查询,最多10次即可得出适合的预测。

结果同样不准确,在我的观点看来,两两一个分类器,若输入样本不在这两类中,就很容易出现误差,可能出现classifier function的值特别大,从而干扰到预测,只是若预测前几个的话会有正确的预测。

4.训练9*10个二分类器(共十类,两类之间有两个分类器,分别为其中之一的正样本分类器)

结果很不稳定,有的预测很准确能到达90%,有的很不准确,在25%左右。

5.一个二分类器(共两类)

输入为两类中的其中之一,这样的话预测的结果很准。



这个算法的实现花了大概两周的时间,前期看文章看了两天,第三天向的师姐请教,算是了解了整个算法的实现过程。之后花了两天时间把patch descriptor提取出来并利用k-means聚成500类,但是到5.3的quantizing features的时候发现量化后500维几乎都为0了,给文章作者Mathias Eitz发了封邮件询问了这个问题,很开心他回复了,让我调大高斯核函数中的sigma试试看,调大之后发现情况并没有好转,虽然为0的维数减少了,但是都是e-100以上的量级,后来又读了一遍文章发现在patch descriptor的提取中忘了对其进行归一化处理,归一化后在进行quantizing features情况有所好转,但是还是有很大一部分为0(我觉得有可能这里导致了之后分类效果差)。之后就进行svm训练预测,效果不太好。最后根据多个不同的svm的处理方法得到了几个不同的预测方法,然后用mfc做了个界面,让大家简单的试试看吧。

The provided references do not contain information related to rendering a sketch animation JSON file in WidgetKit using Swift. To render a sketch animation JSON file in WidgetKit with Swift, one general approach could be as follows: First, parse the JSON file. Swift provides the `JSONDecoder` class which can be used to convert JSON data into Swift objects. For example: ```swift import Foundation struct SketchAnimation: Codable { // Define the properties according to the structure of the sketch animation JSON let someProperty: String // Add other properties as needed } if let url = Bundle.main.url(forResource: "yourSketchAnimation", withExtension: "json") { do { let data = try Data(contentsOf: url) let decoder = JSONDecoder() let animation = try decoder.decode(SketchAnimation.self, from: data) } catch { print("Error decoding JSON: \(error)") } } ``` After parsing the JSON, depending on the nature of the animation data, you may need to use appropriate UIKit or SwiftUI components to render the animation. In WidgetKit, you can use SwiftUI views. For example, if the animation data represents a sequence of frames, you could use a `Timer` to update the displayed frame at regular intervals: ```swift import SwiftUI import WidgetKit struct AnimationWidget: Widget { let kind: String = "AnimationWidget" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: AnimationProvider()) { entry in AnimationWidgetEntryView(entry: entry) } .configurationDisplayName("Animation Widget") .description("Displays a sketch animation") } } struct AnimationWidgetEntry: TimelineEntry { let date: Date let animation: SketchAnimation } struct AnimationProvider: TimelineProvider { func placeholder(in context: Context) -> AnimationWidgetEntry { let dummyAnimation = SketchAnimation(someProperty: "dummy") return AnimationWidgetEntry(date: Date(), animation: dummyAnimation) } func getSnapshot(in context: Context, completion: @escaping (AnimationWidgetEntry) -> ()) { let dummyAnimation = SketchAnimation(someProperty: "dummy") let entry = AnimationWidgetEntry(date: Date(), animation: dummyAnimation) completion(entry) } func getTimeline(in context: Context, completion: @escaping (Timeline<AnimationWidgetEntry>) -> ()) { // Load the real animation JSON here if let url = Bundle.main.url(forResource: "yourSketchAnimation", withExtension: "json") { do { let data = try Data(contentsOf: url) let decoder = JSONDecoder() let animation = try decoder.decode(SketchAnimation.self, from: data) let entry = AnimationWidgetEntry(date: Date(), animation: animation) let timeline = Timeline(entries: [entry], policy: .never) completion(timeline) } catch { let dummyAnimation = SketchAnimation(someProperty: "dummy") let entry = AnimationWidgetEntry(date: Date(), animation: dummyAnimation) let timeline = Timeline(entries: [entry], policy: .never) completion(timeline) } } } } struct AnimationWidgetEntryView: View { var entry: AnimationWidgetEntry var body: some View { // Here you would implement the logic to render the animation based on the entry.animation data Text(entry.animation.someProperty) } } ``` This is a very basic example and the actual implementation would need to be adjusted according to the specific structure and requirements of the sketch animation JSON file.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值