目录

  • 一、人脸图像特征提取方法
  • 二、对笑脸数据集genki4k进行训练和测试(包括SVM、CNN),输出模型训练精度和测试精度(F1-score和ROC),实现检测图片笑脸和实时视频笑脸检测
    • (一)环境、数据集准备
    • (二)训练笑脸数据集genki4k
    • (三)图片笑脸检测
    • (四)实时视频笑脸检测
  • 三、将笑脸数据集换成人脸口罩数据集,并对口罩数据集进行训练,编写程序实现人脸口罩检测
    • (一)训练人脸口罩数据集
    • (二)编程实现人脸口罩检测

一、人脸图像特征提取方法

https://blog.csdn.net/weixin_45137708/article/details/107110857

二、对笑脸数据集genki4k进行训练和测试(包括SVM、CNN),输出模型训练精度和测试精度(F1-score和ROC),实现检测图片笑脸和实时视频笑脸检测

(一)环境、数据集准备

本文操作在Jupyter notebook平台进行,需要安装tensorflow、Keras库、Dlib库、和opencv-python等。
1、安装tensorflow、Keras库
https://blog.csdn.net/weixin_45137708/article/details/106674849
2、安装配置Dlib库和opencv-python
https://blog.csdn.net/jajit/article/details/106630937?utm_source=app
3、笑脸数据集下载
笑脸数据集下载链接:https://pan.baidu.com/s/1ldA8CF6pH4q0Bheik_Tttg
提取码:fui1

(二)训练笑脸数据集genki4k

1、首先导入Keras库

import keras
keras.__version__


2、读取笑脸数据集,然后将训练的数据和测试数据放入对应的文件夹

import os, shutil
# The path to the directory where the original
# dataset was uncompressed
original_dataset_dir = 'C:\\Users\\asus\\Desktop\\test\\genki4k'# The directory where we will
# store our smaller dataset
base_dir = 'C:\\Users\\asus\\Desktop\\test\\smile_and_nosmile'
os.mkdir(base_dir)# Directories for our training,
# validation and test splits
train_dir = os.path.join(base_dir, 'train')
os.mkdir(train_dir)
validation_dir = os.path.join(base_dir, 'validation')
os.mkdir(validation_dir)
test_dir = os.path.join(base_dir, 'test')
os.mkdir(test_dir)# Directory with our training smile pictures
train_smile_dir = os.path.join(train_dir, 'smile')
os.mkdir(train_smile_dir)# Directory with our training nosmile pictures
train_nosmile_dir = os.path.join(train_dir, 'nosmile')
os.mkdir(train_nosmile_dir)# Directory with our validation smile pictures
validation_smile_dir = os.path.join(validation_dir, 'smile')
os.mkdir(validation_smile_dir)# Directory with our validation nosmile pictures
validation_nosmile_dir = os.path.join(validation_dir, 'nosmile')
os.mkdir(validation_nosmile_dir)# Directory with our validation smile pictures
test_smile_dir = os.path.join(test_dir, 'smile')
os.mkdir(test_smile_dir)# Directory with our validation nosmile pictures
test_nosmile_dir = os.path.join(test_dir, 'nosmile')
os.mkdir(test_nosmile_dir)


3、将笑脸图片和非笑脸图片放入对应文件夹
在上面程序中生成了一个名为smile_and_nosmile的文件夹,里面有三个子文件,分别存放训练、测试、验证数据,在这三个文件夹下还有smile和nosmile文件夹,我们需要将笑脸图片放入smile文件夹,将非笑脸图片放入nosmile文件夹。


3、打印每个数据集文件中的笑脸和非笑脸图片数

print('total training smile images:', len(os.listdir(train_smile_dir)))
print('total training nosmile images:', len(os.listdir(train_nosmile_dir)))
print('total validation smile images:', len(os.listdir(validation_smile_dir)))
print('total validation nosmile images:', len(os.listdir(validation_nosmile_dir)))
print('total test smile images:', len(os.listdir(test_smile_dir)))
print('total test nosmile images:', len(os.listdir(test_nosmile_dir)))

4、构建小型卷积网络
我们已经为MNIST构建了一个小型卷积网,所以您应该熟悉它们。我们将重用相同的通用结构:我们的卷积网将是一个交替的Conv2D(激活relu)和MaxPooling2D层的堆栈。然而,由于我们处理的是更大的图像和更复杂的问题,因此我们将使我们的网络相应地更大:它将有一个更多的Conv2D + MaxPooling2D阶段。这样既可以扩大网络的容量,又可以进一步缩小特征图的大小,这样当我们到达平坦层时,特征图就不会太大。在这里,由于我们从大小为150x150的输入开始(有点随意的选择),我们在Flatten层之前得到大小为7x7的feature map。

注意:feature map的深度在网络中逐渐增加(从32到128),而feature map的大小在减少(从148x148到7x7)。这是你会在几乎所有convnets中看到的模式。由于我们解决的是一个二元分类问题,我们用一个单一单元(一个大小为1的稠密层)和一个s型激活来结束网络。这个单元将对网络正在查看一个类或另一个类的概率进行编码。

from keras import layers
from keras import modelsmodel = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu',input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

让我们来看看要素地图的尺寸是如何随每个连续图层而变化的

model.summary()


让我们来看看特征地图的尺寸是如何随着每一个连续的层:为我们编译步骤,我们将一如既往地使用RMSprop优化器。由于我们用一个单一的乙状结肠单元结束我们的网络,我们将使用二进制交叉熵作为我们的损失

from keras import optimizersmodel.compile(loss='binary_crossentropy',optimizer=optimizers.RMSprop(lr=1e-4),metrics=['acc'])

5、数据预处理
在将数据输入到我们的网络之前,应该将数据格式化为经过适当预处理的浮点张量。目前,我们的数据以JPEG文件的形式保存在硬盘上,因此将其导入网络的步骤大致如下:

  • 读取图片文件
  • 解码JPEG内容到RBG像素网格
  • 把它们转换成浮点张量
  • 将像素值(从0到255)缩放到[0,1]区间
from keras.preprocessing.image import ImageDataGenerator# All images will be rescaled by 1./255
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)train_generator = train_datagen.flow_from_directory(# This is the target directorytrain_dir,# All images will be resized to 150x150target_size=(150, 150),batch_size=20,# Since we use binary_crossentropy loss, we need binary labelsclass_mode='binary')validation_generator = test_datagen.flow_from_directory(validation_dir,target_size=(150, 150),batch_size=20,class_mode='binary')

让我们看看其中一个生成器的输出:它生成150×150 RGB图像的批次(Shape(20,150,150,3))和二进制标签(Shape(20,))。20是每批样品的数量(批次大小)。注意,生成器无限期地生成这些批:它只是无休止地循环目标文件夹中的图像。因此,我们需要在某个点中断迭代循环。

for data_batch, labels_batch in train_generator:print('data batch shape:', data_batch.shape)print('labels batch shape:', labels_batch.shape)break


使用生成器使我们的模型适合于数据

history = model.fit_generator(train_generator,steps_per_epoch=100,epochs=30,validation_data=validation_generator,validation_steps=50)


这里使用fit_generator方法来完成此操作,对于我们这样的数据生成器,它相当于fit方法。它期望Python生成器作为第一个参数,它将无限期地生成成批的输入和目标,就像我们的示例一样。因为数据是不断生成的,所以在宣告一个纪元结束之前,生成器需要知道示例从生成器中抽取多少样本。这就是steps_per_epoch参数的作用:在从生成器中绘制完steps_per_epoch批处理之后,即在运行完steps_per_epoch梯度下降步骤之后,拟合过程将转到下一个epoch。在我们的例子中,批次是20个样本大,所以在我们看到2000个样本的目标之前将需要100个批次。

在使用fit_generator时,可以传递validation_data参数,就像fit方法一样。重要的是,允许这个参数本身是一个数据生成器,但是它也可以是Numpy数组的元组。如果您传递一个生成器作为validation_data,那么这个生成器将会不断生成成批的验证数据,因此您还应该指定validation_steps参数,它告诉流程从验证生成器提取多少批来进行评估。

保存模型

model.save('C:\\Users\\asus\\Desktop\\test\\smile_and_nosmile.h5')

在训练和验证数据上绘制模型的损失和准确性

import matplotlib.pyplot as pltacc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']epochs = range(len(acc))plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()plt.figure()plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()


这些图具有过拟合的特点。我们的训练精度随着时间线性增长,直到接近100%,而我们的验证精度停留在70-72%。我们的验证损失在5个epoch后达到最小,然后停止,而训练损失继续线性下降,直到接近0。
6、数据增强
过度拟合是由于可供学习的样本太少,使我们无法训练一个模型来泛化到新的数据。给定无限的数据,我们的模型将暴露于手头数据分布的每一个可能方面:我们永远不会过度拟合。数据增强采用的方法是从现有的训练样本中生成更多的训练数据,方法是通过一系列随机变换来“增强”样本,从而产生看上去可信的图像。我们的目标是在训练时,我们的模型不会两次看到完全相同的图像。这有助于将模型暴露于数据的更多方面,并更好地泛化。

datagen = ImageDataGenerator(rotation_range=40,width_shift_range=0.2,height_shift_range=0.2,shear_range=0.2,zoom_range=0.2,horizontal_flip=True,fill_mode='nearest')
  • rotation_range是一个角度值(0-180),在这个范围内可以随机旋转图片
  • width_shift和height_shift是范围(作为总宽度或高度的一部分),在其中可以随机地垂直或水平地转换图片
  • shear_range用于随机应用剪切转换
  • zoom_range用于在图片内部随机缩放
  • horizontal_flip是用于水平随机翻转一半的图像——当没有假设水平不对称时(例如真实世界的图片)
  • fill_mode是用于填充新创建像素的策略,它可以在旋转或宽度/高度移动之后出现。

查看增强后的图像

# This is module with image preprocessing utilities
from keras.preprocessing import imagefnames = [os.path.join(train_smile_dir, fname) for fname in os.listdir(train_smile_dir)]# We pick one image to "augment"
img_path = fnames[3]# Read the image and resize it
img = image.load_img(img_path, target_size=(150, 150))# Convert it to a Numpy array with shape (150, 150, 3)
x = image.img_to_array(img)# Reshape it to (1, 150, 150, 3)
x = x.reshape((1,) + x.shape)# The .flow() command below generates batches of randomly transformed images.
# It will loop indefinitely, so we need to `break` the loop at some point!
i = 0
for batch in datagen.flow(x, batch_size=1):plt.figure(i)imgplot = plt.imshow(image.array_to_img(batch[0]))i += 1if i % 4 == 0:breakplt.show()


如果我们使用这种数据增加配置训练一个新的网络,我们的网络将永远不会看到两次相同的输入。然而,它看到的输入仍然是高度相关的,因为它们来自少量的原始图像——我们不能产生新的信息,我们只能混合现有的信息。因此,这可能还不足以完全消除过度拟合。

为了进一步对抗过拟合,我们还将在我们的模型中增加一个Dropout层,就在密集连接分类器之前:

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu',input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))model.compile(loss='binary_crossentropy',optimizer=optimizers.RMSprop(lr=1e-4),metrics=['acc'])

用数据增强和退出来训练我们的网络:

train_datagen = ImageDataGenerator(rescale=1./255,rotation_range=40,width_shift_range=0.2,height_shift_range=0.2,shear_range=0.2,zoom_range=0.2,horizontal_flip=True,)# Note that the validation data should not be augmented!
test_datagen = ImageDataGenerator(rescale=1./255)train_generator = train_datagen.flow_from_directory(# This is the target directorytrain_dir,# All images will be resized to 150x150target_size=(150, 150),batch_size=32,# Since we use binary_crossentropy loss, we need binary labelsclass_mode='binary')validation_generator = test_datagen.flow_from_directory(validation_dir,target_size=(150, 150),batch_size=32,class_mode='binary')history = model.fit_generator(train_generator,steps_per_epoch=100,epochs=100,validation_data=validation_generator,validation_steps=50)


这里程序会跑很久,我跑了几个小时,用GPU跑会快很多很多。

保存模型在convnet可视化部分使用:

model.save('C:\\Users\\asus\\Desktop\\test\\smile_and_nosmile_1.h5')

再看一次结果

acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']epochs = range(len(acc))plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()plt.figure()plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()


由于数据的增加和遗漏,我们不再过度拟合:训练曲线相当紧密地跟踪验证曲线。我们现在能够达到82%的精度,相对于非正则化模型有15%的改进。通过进一步利用正则化技术和调整网络参数(比如每个卷积层的滤波器数量,或者网络中的层数),我们可能能够获得更好的精度,可能达到86-87%。

7、优化提高笑脸图像分类模型精度
构建卷积网络

from keras import layers
from keras import models
from keras import optimizers
model = models.Sequential()
#输入图片大小是150*150 3表示图片像素用(R,G,B)表示
model.add(layers.Conv2D(32, (3,3), activation='relu', input_shape=(150 , 150, 3)))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(64, (3,3), activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128, (3,3), activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128, (3,3), activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer=optimizers.RMSprop(lr=1e-4),metrics=['acc'])
model.summary()

(三)图片笑脸检测

# 单张图片进行判断  是笑脸还是非笑脸
import cv2
from keras.preprocessing import image
from keras.models import load_model
import numpy as npmodel = load_model('smile_and_nosmile_1.h5')img_path='C:\\Users\\asus\\Desktop\\test\\genki4k\\file2227.jpg'img = image.load_img(img_path, target_size=(150, 150))
#img1 = cv2.imread(img_path,cv2.IMREAD_GRAYSCALE)
#cv2.imshow('wname',img1)
#cv2.waitKey(0)#print(img.size)
img_tensor = image.img_to_array(img)/255.0
img_tensor = np.expand_dims(img_tensor, axis=0)prediction =model.predict(img_tensor)
print(prediction)
if prediction[0][0]>0.5:result='smile'
else:result='nosmile'
print(result)



结果正确,错误率在0.0883181左右,反复找图片尝试,结果都是正确的。

(四)实时视频笑脸检测

import cv2
from keras.preprocessing import image
from keras.models import load_model
import numpy as np
import dlib
from PIL import Image
model = load_model('smile_and_nosmile_1.h5')
detector = dlib.get_frontal_face_detector()
video=cv2.VideoCapture(0)
font = cv2.FONT_HERSHEY_SIMPLEX
def rec(img):gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)dets=detector(gray,1)if dets is not None:for face in dets:left=face.left()top=face.top()right=face.right()bottom=face.bottom()cv2.rectangle(img,(left,top),(right,bottom),(0,255,0),2)img1=cv2.resize(img[top:bottom,left:right],dsize=(150,150))img1=cv2.cvtColor(img1,cv2.COLOR_BGR2RGB)img1 = np.array(img1)/255.img_tensor = img1.reshape(-1,150,150,3)prediction =model.predict(img_tensor)    print(prediction)if prediction[0][0]<0.5:result='nosmile'else:result='smile'cv2.putText(img, result, (left,top), font, 2, (0, 255, 0), 2, cv2.LINE_AA)cv2.imshow('smile detector', img)
while video.isOpened():res, img_rd = video.read()if not res:breakrec(img_rd)if cv2.waitKey(1) & 0xFF == ord('q'):break
video.release()
cv2.destroyAllWindows()


视频检测正确,就是背景太黑了…

三、将笑脸数据集换成人脸口罩数据集,并对口罩数据集进行训练,编写程序实现人脸口罩检测

(一)训练人脸口罩数据集

人脸口罩数据集下载链接:https://pan.baidu.com/s/11PBCmDDx7Dtx_ckjwZR2uw
提取码:n2um
训练人脸口罩数据集和训练笑脸数据集一样,只需改一下和相应的变量名和数据集。

(二)编程实现人脸口罩检测

import cv2
from keras.preprocessing import image
from keras.models import load_model
import numpy as np
import dlib
from PIL import Image
model = load_model('mask_and_nomask.h5')
detector = dlib.get_frontal_face_detector()
video=cv2.VideoCapture(0)
font = cv2.FONT_HERSHEY_SIMPLEX
def rec(img):gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)dets=detector(gray,1)if dets is not None:for face in dets:left=face.left()top=face.top()right=face.right()bottom=face.bottom()cv2.rectangle(img,(left,top),(right,bottom),(0,255,0),2)img1=cv2.resize(img[top:bottom,left:right],dsize=(150,150))img1=cv2.cvtColor(img1,cv2.COLOR_BGR2RGB)img1 = np.array(img1)/255.img_tensor = img1.reshape(-1,150,150,3)prediction =model.predict(img_tensor)    print(prediction)if prediction[0][0]>0.5:result='nomask'else:result='mask'cv2.putText(img, result, (left,top), font, 2, (0, 255, 0), 2, cv2.LINE_AA)cv2.imshow('mask detector', img)
while video.isOpened():res, img_rd = video.read()if not res:breakrec(img_rd)if cv2.waitKey(1) & 0xFF == ord('q'):break
video.release()
cv2.destroyAllWindows()

运行结果:
不戴口罩

戴口罩

人脸口罩检测正确!

虽然人脸口罩检测正确,但是精度还是不高,因为我的数据集里面戴口罩得的图像太少了,朋友们可以多找一些戴口罩的图片以提高精度,后续我也会不断完善~

Python实现笑脸检测+人脸口罩检测相关推荐

  1. Python实现人脸口罩检测!这玩意太强大了啊!

    由于疫情的影响,人脸口罩检测系统的开发成为很多人争相开发的一种算法.很多公司或者个人都开源了他们很多的代码或者SDK.大家在GitHub或者各种平台上都能够找到很多的资源. 前段时间在博客上看到几个有 ...

  2. python实现人脸口罩检测(基于opencv和深度学习两种方法)

    人脸口罩检测GUI系统(基于opencv和深度学习两种方法对比) 由于疫情的影响,人脸口罩检测系统的开发成为很多人争相开发的一种算法.很多公司或者个人都开源了他们很多的代码或者SDK.大家在GitHu ...

  3. 人脸口罩检测现开源PyTorch、TensorFlow、MXNet等全部五大主流深度学习框架模型和代码...

    号外!号外! 现在,AIZOO开源PyTorch.TensorFlow.MXNet.Keras和Caffe五大主流深度学习框架的人脸检测模型和代码啦! 先附上Github链接为敬. https://g ...

  4. 基于Opencv-python人脸口罩检测(附完整代码)

    目录 一.开发环境 二.设计要求 三.设计原理 四.程序代码 五.结果展示 六.结论 一.开发环境 python 3.6.6 opencv-python 4.5.1 二.设计要求 · 1.使用open ...

  5. 基于HSV+HOG特征和SVM的人脸口罩检测算法

    基于HSV+HOG特征和SVM的人脸口罩检测算法 基于python语言编写,使用retinaface检测人脸和定位五个特征点,利用特征点进一步定位口鼻区域,提取该区域的HSV特征和HOG特征,使用SV ...

  6. 五分钟快速搭建一个实时人脸口罩检测系统(OpenCV+PaddleHub 含源码)

    导读 本文主要介绍如何使用OpenCV和PaddleHub实现一个实时人脸口罩检测系统.(公众号:OpenCV与AI深度学习) 背景介绍 从19年疫情爆发到现在,佩戴口罩对大家来说已是常态.应运而生的 ...

  7. AIZOO开源人脸口罩检测

    帮一个创业的小伙伴转的他们自己开源的一个人脸戴口罩检测模型,效果还不错,支持一下,嘿嘿!原文链接在此 近一个月来,新冠肺炎疫情牵动着全国人民的心,一线医护工作者在最前线抗击疫情的同时,我们也可以看到很 ...

  8. c语言人脸口罩检测,使用ModelArts 0代码实现人脸口罩检测

    一.灵感来源 前两天闲逛华为Modelarts AI市场的时候偶然间发现huqi大佬上传的一个有关口罩检测的数据集,突然就想着看见好多大佬写过口罩检测的案例,要不我也玩一把试试.虽然没有大佬们高大上都 ...

  9. 基于Python,dlib实现人脸关键点检测

    @代码实现及安装过程 基于Python,dlib实现人脸关键点检测 dilb 在做人脸检测人脸识别方面用到比较多的.face_recognition就是基于dlib实现的. 这篇文章将使用Python ...

  10. 基于YOLOV5的目标检测模型-口罩检测

    好文章 在学习的时候参考了许多大佬的文章,我会在下面一 一列出来 1.手把手教你使用YOLOV5训练自己的目标检测模型-口罩检测-视频教程_肆十二的博客-CSDN博客_opencv yolo训练自己模 ...

最新文章

  1. Oracle数据库中有关记录个数的查询
  2. C语言-动态创建二维数组
  3. SendKeys中特殊字符的键代码
  4. EIGRP非等价负载均衡
  5. Java 7:使用NIO.2进行文件过滤–第3部分
  6. c语言课程设计六角填数,关于蓝桥杯C语言B组的六角型答案问题
  7. [翻译]进化游戏的层次结构 - 用组件来重构你的游戏实体
  8. ps -ef | grep 命令详解
  9. 【生信技能树】GEO数据库挖掘 P5
  10. Shell脚本攻略04-玩转文件描述符及重定向
  11. java.io.IOException Failed to replace a bad datanode
  12. 北京2008年第29届奥运会吉祥物五个福娃(组图)
  13. Abbkine ExKine 胞浆蛋白提取试剂盒
  14. 微信接口php oa,你必须了解OA与微信结合的几种方式
  15. wangEditor富文本编辑器使用、编辑器内容转json格式
  16. 逆向分析工具-APKtool+dex2jar+jd-gui下载安装
  17. CentOS7 安装Chrome
  18. java得物公司面试怎么样,得物app
  19. 很jiong的一天又过去了
  20. 用一个智能魔方,转动IP网络的时代变局

热门文章

  1. 保存远程图片到本地 同时取得第一张图片并创建缩略图
  2. 2.软件架构设计:大型网站技术架构与业务架构融合之道 --- 架构的道与术
  3. jquery中animate()函数不能调用元素的display属性
  4. Ubuntu下TP5隐藏入口文件
  5. Scala学习——隐式转换
  6. 家庭记账本开发记录(4)
  7. 【CSS基础笔记】——盒模型、块级元素、行内元素、浮动、对齐、定位
  8. java类型占用字节数类型转换
  9. Novernber Rain
  10. [WP8] Binding时,依照DataType来选择DataTemplate