圣人男 2018-08-29
虽然相机作为捕捉图像的工具已有150多年的历史,但智能手机的出现极大地改变了相机的使用,并将其与软件编程集成在一起。如今,相机不再完成“简单”任务,例如拍摄静止照片或拍摄视频,它们实际上复制了人眼执行的大部分复杂任务。
事实上,计算机视觉中使用的大多数算法和公式都是基于对人眼的感知。例如,通过对R,B和G像素值求平均来对RGB图像进行灰度级处理,将图像的通道减少到1通道的方法,使用以下公式:灰色←0.299⋅R+0.587⋅G+ 0.114 ⋅B。注意:显然与平均R,G和B像素值不同(如公式所示:Gray←0.33⋅R+0.33⋅G+0.33⋅B)。
但为什么?这是因为与色度相比,我们的眼睛对亮度/亮度更敏感。灰度级图像时使用的实际公式与计算给定像素的亮度/亮度值相同。注意:亮度值是YUV和YCrCb颜色空间中的Y值。
图1
自动对焦是相机的一个有趣功能。它实际上可以复制我们眼睛的聚焦能力。为了更好地可视化,请在不同平面上拍摄两个物体(水瓶和插座),如图1所示。现在把注意力集中在水瓶上。不知不觉中,你的眼睛模糊了后面的墙壁插座。对墙壁插座重复相同的操作,我们的眼睛会自动模糊水瓶。现在在家中复制类似的设置并打开手机的相机应用程序。通过点击您想要关注的区域,相机会像我们的眼睛一样自动模糊其他物体。这是相机自动对焦的神奇之处。今天,相机使用许多算法来聚焦图像。我将重点介绍一种基本的自动对焦算法:基于对比度的自动对焦。
基于对比度的自动对焦基本上测量不同相机镜头位置的对比度。相机镜头将一直移动,直到达到其定义为具有最大对比度的点。我们可以将对比度定义为图像中的锐度。为了更好地可视化,这里是各种相机镜头位置的虚拟图像(比较黑色方块的边缘):
图2
为了概括对比度,我们可以为每个图像分配0到1之间的任意对比度值,其中0表示未聚焦/低对比度图像,1表示聚焦/高对比度图像。
图3
现在,相机循环通过变化的相机镜头位置,计算图像内的对比度,然后比较并选择具有最高对比度值的相机镜头位置。
有许多不同的方法来定义对比度。实际上,算法的复杂性是基于用于定义对比度的方法确定的。今天我将介绍一种非常基本的方法,它不需要额外的硬件,如传感器。请注意,公司通常利用手机内置的其他硬件组件来提高算法的复杂性。
为图像指定对比度值的一种基本方法是Canny边缘检测。实际上,这就是OpenCV如何计算自动对焦的最佳相机镜头位置。对于每个镜头位置,我们绘制图像的边缘并根据边缘的清晰度分配对比度值。例如,下面的图像序列显示执行Canny边缘检测算法后的输出:
图4
如您所见,图像聚焦越少,边缘越不清晰。事实上,在设置Canny边缘检测算法的默认设置后,您会注意到它无法检测左侧两个日益未聚焦的图像的边缘。显然,通过降低算法的最小阈值,我们将能够看到边缘。但是为了自动对焦,我们可以丢弃不检测边缘的相机镜头位置。
如果您想进一步探索OpenCV的自动聚焦算法,请查看以下代码:https://github.com/opencv/opencv/blob/master/samples/cpp/autofocus.cpp。
基于对比度的自动对焦算法需要许多计算 - > L * O(对比度),其中L是可能的相机镜头位置的数量,O(对比度)是用于确定图像内对比度的算法的复杂度。在上面的例子中,确定边缘是否清晰的复杂性需要大于O(m * n),其中m和n是帧的尺寸。由于这种高复杂性,许多创建相机驱动程序(Qualcomm,Apple等)的公司研究新的和更有效的方法来确定图像是否聚焦。一些其他常见的算法包括PDAF,激光等。事实上,作为一个侧面实验,
作为扩展,我决定使用基于机器学习的方法来定义对比度。因为每帧的计算边缘计算量很大,所以我决定设计一个神经网络,这应该会降低基于对比度的自动对焦算法的复杂性。
我决定尝试前馈和卷积神经网络。
我开始尝试使用前馈神经网络,因为这更容易实现。我创建了一个4层的小型神经网络。在这个神经网络中,我为一个大小为224乘224像素的3通道图像添加了一个输入层。然后我继续将尺寸flatten 为单个维度,并添加了1024个神经元的隐藏层。我没有直接跳到最后一层,而是决定追加128个神经元的另一个隐藏层。最后,我用2个神经元的输出层完成了神经网络,以指示聚焦或未聚焦。
主要Python代码如下:
from keras import layers
my_model = keras.models.Sequential()
my_model.add(layers.Dense(3, input_shape=(224,224, 3), activation='relu'))
my_model.add(layers.Flatten())
my_model.add(layers.Dense(1024, activation='relu'))
my_model.add(layers.Dropout(0.5))
my_model.add(layers.Dense(128, activation='relu'))
my_model.add(layers.Dropout(0.5))
my_model.add(layers.Dense(2, activation='softmax'))
my_model.summary()
'''
SET UP TRAINING AND VALIDATION DATA
'''
from keras.preprocessing.image import ImageDataGenerator, load_img
train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
horizontal_flip=True,
vertical_flip=True,
fill_mode='nearest')
validation_datagen = ImageDataGenerator(rescale=1./255)
train_batchsize = 20
val_batchsize = 5
train_generator = train_datagen.flow_from_directory(
'train_data',
target_size=(224, 224),
batch_size=train_batchsize,
class_mode='categorical')
validation_generator = validation_datagen.flow_from_directory(
'validation_data',
target_size=(224, 224),
batch_size=val_batchsize,
class_mode='categorical',
shuffle=False)
# Compile
try:
my_model = keras.models.load_model('autofocus_ff.h5') # trains from last epoch if model exists
except:
print "Training new model..."
my_model.compile(loss='categorical_crossentropy',
optimizer='adam',
metrics=['acc'])
# Train
checkpoint = keras.callbacks.ModelCheckpoint('autofocus_ff.h5', monitor='val_loss', verbose=1, period=1)
my_model_hist = my_model.fit_generator(
train_generator,
steps_per_epoch=2*train_generator.samples/train_generator.batch_size ,
epochs=25,
validation_data=validation_generator,
validation_steps=validation_generator.samples/validation_generator.batch_size,
verbose=1,
callbacks=[checkpoint])
# Save the Model
my_model.save('autofocus_ff.h5')
在我开始训练这个前馈神经网络后,我发现模型的验证准确性和损失在每个时期都不再提高。
这促使我尝试使用卷积神经网络(CNN),因为它们似乎可以为物体检测提供更好的结果。Keras有许多预先训练过的CNN模型,我选择根据给定的问题微调vgg16模型。我采用了vgg16模型的5个卷积块,然后添加了一个全局平均池图层,将尺寸减小到1,同时还有效地减少了可训练参数的数量。最后,我添加了一个2个神经元的输出层来表示聚焦或未聚焦的置信度。
Python实现的过程如下:
from keras.applications import VGG16
#Load the VGG model
vgg16 = VGG16(include_top=False, input_shape=(224, 224, 3)) # load vgg16 with all defaults except top
'''
MARK ALL LAYERS AS NON-TRAINABLE SO TRAINING DOESN'T ALTER WEIGHTS
'''
for layer in vgg16.layers:
layer.trainable = False
'''
TUNE MODEL TO AUTOFOCUS NEEDS
'''
from keras import layers
# my_model_output = vgg16.layers[10].output # Only use the first three blocks of convolution
my_model_output = vgg16.output # use all 5 conv layers of vgg
my_model_output = layers.GlobalAveragePooling2D()(my_model_output) # Then add a GlobalAveragePooling layer to get 1d representation
# my_model_output = layers.Dense(128, activation='relu')(my_model_output) # add a Dense layer just to increase trainable params
# my_model_output = layers.Dropout(0.5)(my_model_output) # Add regularization to help decrease overfitting data
my_model_output = layers.Dense(2, activation='softmax')(my_model_output) # binary classification prediction layer
my_model = keras.models.Model(inputs=vgg16.input, outputs=my_model_output) #created my neural network
my_model.summary()
'''
SET UP TRAINING AND VALIDATION DATA
'''
from keras.preprocessing.image import ImageDataGenerator, load_img
train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
horizontal_flip=True,
vertical_flip=True,
fill_mode='nearest')
validation_datagen = ImageDataGenerator(rescale=1./255)
train_batchsize = 20
val_batchsize = 5
train_generator = train_datagen.flow_from_directory(
'train_data',
target_size=(224, 224),
batch_size=train_batchsize,
class_mode='categorical')
validation_generator = validation_datagen.flow_from_directory(
'validation_data',
target_size=(224, 224),
batch_size=val_batchsize,
class_mode='categorical',
shuffle=False)
'''
COMPILE AND TRAIN MODEL
'''
# Compile
try:
my_model = keras.models.load_model('autofocus_vgg16.h5') # trains from last epoch if model exists
except:
print "Training new model..."
my_model.compile(loss='categorical_crossentropy',
optimizer='adam',
metrics=['acc'])
# Train
checkpoint = keras.callbacks.ModelCheckpoint('autofocus_vgg16.h5', monitor='val_loss', verbose=1, period=1)
my_model_hist = my_model.fit_generator(
train_generator,
steps_per_epoch=2*train_generator.samples/train_generator.batch_size ,
epochs=25,
validation_data=validation_generator,
validation_steps=validation_generator.samples/validation_generator.batch_size,
verbose=1,
callbacks=[checkpoint])
# Save the Model
my_model.save('autofocus_vgg16.h5')
# Compile
try:
my_model = keras.models.load_model('autofocus_vgg16.h5') # trains from last epoch if model exists
except:
print "Training new model..."
my_model.compile(loss='categorical_crossentropy',
optimizer='adam',
metrics=['acc'])
# Train
checkpoint = keras.callbacks.ModelCheckpoint('autofocus_vgg16.h5', monitor='val_loss', verbose=1, period=1)
my_model_hist = my_model.fit_generator(
train_generator,
steps_per_epoch=2*train_generator.samples/train_generator.batch_size ,
epochs=25,
validation_data=validation_generator,
validation_steps=validation_generator.samples/validation_generator.batch_size,
verbose=1,
callbacks=[checkpoint])
# Save the Model
my_model.save('autofocus_vgg16.h5')
'''
PREDICT
'''
import numpy as np
import keras
from keras.models import load_model
from keras.preprocessing import image
model = keras.models.load_model('autofocus_vgg16.h5')
def load_image_as_tensor(img_path):
img = image.load_img(img_path, target_size=(224, 224))
img = image.img_to_array(img)
img = np.expand_dims(img, axis=0)
img /= 255.
return img
def predict(img_path):
img = load_image_as_tensor(img_path)
prob = model.predict(img)
classes = np.argmax(prob, axis=1)
label_map = (validation_generator.class_indices)
print img_path
print prob, classes, label_map
predict('test_blur/black_square.jpg')
predict('test_blur/black_square9.jpg')
predict('test_blur/black_square17.jpg')
predict('test_blur/black_square25.jpg')
predict('test_blur/black_square33.jpg')
predict('test_blur/black_square41.jpg')
# 100% Accurate for these 6 test images
与前馈神经网络相比,该模型的准确性要好得多,我完成了25个时期,验证准确率为0.76,验证损失为0.49。虽然这些数字不是很好,但我觉得这些数字反映了缺乏训练数据。我只使用了1400张训练图像,如果我将其增加到大约6000-7000张图像,我觉得我的准确度会有显着提高。我在图2所示的6个图像上测试了我的模型。模型正确地将每个图像标记为聚焦或未聚焦。