我一直致力于将图像作为输入的顺序模型。然而,不同的是输入图像实际上是由键确定的 例如,训练序列是(您可以假设 fi 是视频的帧ID)
{ f1, f2, f3, ..., fn }
并且相应的图像序列是
{ M[f1], M[f2], M[f3], ..., M[fn] }
其中M是存储{ fi->图像}映射的地图 假设在下一批中,我的训练序列成为
{ f2, f3, ..., fn+1 }
,图像序列变为
{ M[f2], M[f3], M[f4], ..., M[fn+1] }
如您所见,如果我直接将图像序列保存到磁盘中,则存在大量冗余(在上述情况下,M [f2]到M [fn]被保存两次)。因此,似乎有必要通过键引用图像,因此不能使用imagedataloader类
[修改]
我的模型是 2级分类器,将图像序列作为输入,其中图像使用帧ID ( fi )进行映射。在 data_preprocess 代码中预先生成图像序列是正还是负
正面样本可能如下所示:
{f3, f4, f5, f6, f7} 1
{f4, f5, f6, f7, f8} 1
{f5, f6, f7, f8, f9} 1
...
否定样本如下所示:
{f1, f2, f3, f4, f5} 0
{f2, f3, f4, f5, f6} 0
{f10, f11, f12, f13, f14} 0
...
因此,它不像图像分类问题,其中图像具有完全固定的标签。在我的情况下,每个图像将被使用多次,它们的正面或负面由整个序列决定,但不是由它自己决定。
[ EDIT II ]
图像是N个视频的帧,并存储在磁盘上,如下所示:
|-data_root/
|-Video 1/
| |-frame_1_1.jpg
| |-frame_1_2.jpg
| ...
|-Video 2/
| |-frame_2_1.jpg
| |-frame_2_2.jpg
| ...
...
...
|-Video N/
| |-frame_N_1.jpg
| |-frame_N_2.jpg
...
我想做的是,给定两个场景的帧/图像序列,模型预测两个场景是否属于同一类型。
由于视频可能包含每个场景的长时间跨度,因此我将场景的整个序列划分为多个非重叠子序列(省略视频索引):
Sequence of scene i: frame_1, frame_2, frame_3, ..., frame_n
Sub-sequence i_1: frame_1, frame_2, frame_3, ..., frame_10
Sub-sequence i_2: frame_11, frame_12, frame_13, ..., frame_20
Sub-sequence i_3: frame_21, frame_22, frame_23, ..., frame_30
...
然后,我随机生成阳性样本Pi(由相同序列生成的子序列对),如:
<Pair of sub-sequences> <Labels>
P1 {sub-sequence i_4, sub-sequence i_2}, 1
P2 {sub-sequence i_3, sub-sequence i_5}, 1
... ...
对于阴性样本,我从不同的场景中生成子序列(Ni):
<Pair of sub-sequences> <Labels>
N1 {sub-sequence i_1, sub-sequence j_6}, 0
N2 {sub-sequence i_2, sub-sequence j_4}, 0
... ...
很明显,一个帧/图像可以在不同的训练样本中多次出现。例如。在上述情况下,N2和P1都包含子序列i_2。因此,我选择按帧ID序列( fi )保存生成的样本对,并在训练期间,按帧ID( fi )获取序列的相应帧/图像。
我应该如何优雅地与Keras一起做?
答案 0 :(得分:4)
不确定如何构建序列但是您是否考虑过使用ImageDataGenerator
from keras.preprocessing.image
?
使用所需的任何参数构建此对象后,可以使用flow_from_directory(directory_path)
方法。完成此操作后,您可以使用此对象的filename属性:
my_generator = ImageDataGenerator(...)
my_generator.flow_from_directory(path_dir)
list_of_file_names = my_generator.filename
现在,您可以在列表的索引和列表的元素(= file_paths)之间建立映射。
我希望这有帮助吗?
编辑:
由此,您可以构建一个映射字典
map_images = {str(os.path.splitext(os.path.split(file_path)[1])[0]): file_path for file_path in list_of_file_names}
这将使用ImageDataGenerator从您的图像文件夹中检索file_path
,它会提取文件名,删除文件扩展名并将文件名转换为string
,这是您的frame_id。
您现在可以在frame_id
和file_path
之间使用load_img()
and img_to_array()
from keras.preprocessing.image
函数load_img()
定义如下并返回PIL图像实例:
def load_img(path, grayscale=False, target_size=None): """Loads an image into PIL format. # Arguments path: Path to image file grayscale: Boolean, whether to load the image as grayscale. target_size: Either 'None' (default to original size) or tuple of ints '(img_height, img_width)'. # Returns A PIL Image instance. # Raises ImportError: if PIL is not available. """
然后img_to_array()
定义如下并返回一个3D numpy数组来提供模型:
def img_to_array(img, dim_ordering='default'): """Converts a PIL Image instance to a Numpy array. # Arguments img: PIL Image instance. dim_ordering: Image data format. # Returns A 3D Numpy array. # Raises ValueError: if invalid 'img' or 'dim_ordering' is passed. """
总结一下:1在frame_id和相应文件的路径之间建立一个映射。然后使用img_load()
和img_to_array()
加载文件。我希望我能正确理解你的问题!
编辑2:
看到您的新编辑,现在我了解了您的文件系统的结构,我们甚至可以在您的字典中添加这样的视频:
# list of video_id of each frame
videos = my_generator.classes
# mapping of the frame_id to path_of_file and vid_id
map_images = {str(os.path.splitext(os.path.split(file_path)[1])[0]): (file_path, vid_id) for file_path,vid_id in zip(list_of_file_names,videos) }