TFRecords文件实现不定长图片和标签的存储和读取感悟(1)(附完整代码)

最近一段时间接触到用tfrecord储存数据和读取,期间踩了数之不尽的坑,在消bug的路上艰难行走,所以在这里记录下我所遇见过的各种坑,望共勉。 

TFRecord是谷歌推荐的一种二进制文件格式,理论上它可以保存任何格式的信息。使用tfrecord时,实际上是先读取原生数据,然后转换成tfrecord格式,在存储在硬盘上。以后使用数据时,就可以从tfrecord文件 解码读出。

TFRecords文件中包含了类型为tf.train.Example的协议内存块(protocol buffer),而在协议内存块中又包含了字段features(tf.train.Features)。features中又包含了若干个feature,每一个feature是一个map,也就是key-value的键值对, key取值是String类型,而value是Feature类型的消息体,它包含三种,BytesList,FloatList和Int64List,它们都是列表list的形式。如下面的函数int64_feature和int64_list_feature,两者最大的区别在于前者是value=[value]和后者的value=value, []表示列表。

def int64_feature(value):
    return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))

def int64_list_feature(value):
    return tf.train.Feature(int64_list=tf.train.Int64List(value=value))
def bytes_feature(value):
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))
example = tf.train.Example(features=tf.train.Features(feature={
    'label': int64_list_feature(image_label),
    'image': bytes_feature(image),
    'h': int64_feature(shape[0]),
    'w': int64_feature(shape[1]),
    'c': int64_feature(shape[2])
}))

如上所定义的Example消息体,包含一张图片image的信息,及其标签label信息和shape大小信息(height, width, channel),在这里和大多数博客里不一样的在于‘label’标签,通常的数据标签是一个整数,例如猫狗图片,用‘0’表示猫,用‘1’表示狗,即使是多分类标签,也可以用0-N来表示各个类别,而我们的图像标签是一串中文或者英文,长度不一,首先在字典中查找其对应下标,形成list数组。关于这个标签的处理,所以在这里提供了两个解决方案(都是踩过的坑):

  • 方案一是将标签list数组转换成one-hot形式,不使用tensorflow的tf.one-hot表示,而是自己定义函数,最后使得每个类别标签为一个字典大小的向量,读取时, feature中定义'label': tf.FixedLenFeature([VOCUBLARY_SIZE], tf.int64),如果不加大小VOCUBLARY_SIZE,会报错
  • 方案二是针对tf.nn.ctc_loss中labels参数的SparseTensor稀疏张量的要求,上述方案一得到的虽然是一个类one-hot形式,终究不是稀疏张量,所以将读取到的label直接传给参数 时还是labels时,是要报错,所以为了得到稀疏向量,直接将标签存储,读取时,feature中定义'label': tf.VarLenFeature(dtype=tf.int64),使用的是变长读取,这样得到的是一个稀疏张量SparseTensor

另一个特殊之处在于图片shape信息的储存,可以看到这里不是直接存储shape,而是分开存储,因为每一个图片的尺寸大小不同,所以如果直接以shape的大小存储,也同样会报错。所以在这里定义了三个键值对。读取image时,就可以使用读取的h,w,c这三个数据reshape图像,如果图像是定长的,shape的大小就可以直接定义,例如shape=[224, 224, 3]等等。

h = tf.cast(image_features['h'], tf.int32)
w = tf.cast(image_features['w'], tf.int32)
c = tf.cast(image_features['c'], tf.int32)

image = tf.decode_raw(image_features['image'], tf.uint8)
image = tf.cast(image, tf.float32)
image = tf.reshape(image, shape=[h, w, c])

最后说到图像image方面,现在面对是image尺寸不一,目的是要图片的高要resize到同一大小,宽度不定长。因为要读取数据时,数据量巨大,程序每次运行时是需要分batch,而每一batch里面要求大小一致,所以如果对image不处理,也是会报错。

  •  第一种情况是我碰见的image数据,height大小一致,宽度不定长,这样存储时,不用对数据进行resize,只是数据读取时,对每一个图像reshape后,使用resized_image = tf.image.resize_image_with_crop_or_pad(image, target_height=32, target_width=max_width)对图像进行填充,虽说有剪裁,但是剪裁后会影响结果,所以这里max_width的设定要尽可能包含所有的图片的宽度,这样后面在对图像进行reshape后resized_image = tf.reshape(resized_image, shape=[32, max_width, 3])就可以batch数据了
  • 第二种情况是碰见的image数据长宽各不一,所以在存储前就需要对image进行resize,等比例缩小。后面处理和第一种情况类似

关于存储成tfrecord的步骤和读取tfrecord文件步骤,现在已经有很多博客进行详细描述,我就不用过多赘述,下面粘贴的是完整的代码。这个代码得到的tfrecord文件要比原图像文件大10倍左右,原图像有2.2G左右,生成的tfrecord文件大约有22G,网上也有人给出答案,例如一个图像共有h*w*c个像素,将图片转变成byte类型时,这些像素按顺序存在一个二进制序列中,每个像素需要用一个的字符进行表示,所以这样就使得图像文件存储变大,如何解决,tensorflow中提供了一个tf.gfile.FastGFile类,可以直接读取图像的bytes形式

tf.gfile.FastGFile(filename, 'rb').read()

'r'表示要从文件中读取数据,‘b’表示要读取二进制数据,但是由于我们数据的复杂性,所以就不再尝试此种方法了。

import tensorflow as tf
from PIL import Image
import numpy as np
import os
import random
from config import CHAR_VECTOR
from config import NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN
from config import NUM_EXAMPLES_PER_EPOCH_FOR_TEST

VOCUBLARY_SIZE = len(CHAR_VECTOR)


def resize_image(image):
    '''resize the size of image'''
    width, height = image.size
    ratio = 32.0 / float(height)
    image = image.resize((int(width * ratio), 32))
    return image


def generation_vocublary(CHAR_VECTOR):
    vocublary = {}
    index = 0
    for char in CHAR_VECTOR:
        vocublary[char] = index
        index = index + 1
    return vocublary


def int64_feature(value):
    return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))


def int64_list_feature(value):
    return tf.train.Feature(int64_list=tf.train.Int64List(value=value))


def bytes_feature(value):
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))


def generation_TFRecord(data_dir):
    vocublary = generation_vocublary(CHAR_VECTOR)

    image_name_list = []
    for file in os.listdir(data_dir):
        if file.endswith('.jpg'):
            image_name_list.append(file)

    random.shuffle(image_name_list)
    capacity = len(image_name_list)

    # 生成train tfrecord文件
    train_writer = tf.python_io.TFRecordWriter('./dataset/train_dataset.tfrecords')
    train_image_name_list = image_name_list[0:int(capacity * 0.9)]
    for train_name in train_image_name_list:
        train_image_label = []
        for s in train_name.strip('.jpg'):
            train_image_label.append(vocublary[s])

        train_image = Image.open(os.path.join(data_dir, train_name))
        train_image = resize_image(train_image)
        # print(image.size)
        train_image_array = np.asarray(train_image, np.uint8)
        train_shape = np.array(train_image_array.shape, np.int32)
        train_image = train_image.tobytes()

        train_example = tf.train.Example(features=tf.train.Features(feature={
            'label': int64_list_feature(train_image_label),
            'image': bytes_feature(train_image),
            'h': int64_feature(train_shape[0]),
            'w': int64_feature(train_shape[1]),
            'c': int64_feature(train_shape[2])
        }))
        train_writer.write(train_example.SerializeToString())
    train_writer.close()

    # 生成test tfrecord文件
    test_writer = tf.python_io.TFRecordWriter('./dataset/test_dataset.tfrecords')
    test_image_name_list = image_name_list[int(capacity * 0.9):capacity]
    for test_name in test_image_name_list:
        test_image_label = []
        for s in test_name.strip('.jpg'):
            test_image_label.append(vocublary[s])

        test_image = Image.open(os.path.join(data_dir, test_name))
        test_image = resize_image(test_image)
        # print(image.size)
        test_image_array = np.asarray(test_image, np.uint8)
        test_shape = np.array(test_image_array.shape, np.int32)
        test_image = test_image.tobytes()

        test_example = tf.train.Example(features=tf.train.Features(feature={
            'label': int64_list_feature(test_image_label),
            'image': bytes_feature(test_image),
            'h': int64_feature(test_shape[0]),
            'w': int64_feature(test_shape[1]),
            'c': int64_feature(test_shape[2])
        }))
        test_writer.write(test_example.SerializeToString())
    test_writer.close()


def read_tfrecord(filename, max_width, batch_size, train=True):
    filename_queue = tf.train.string_input_producer([filename])
    reader = tf.TFRecordReader()
    _, serialize_example = reader.read(filename_queue)
    image_features = tf.parse_single_example(serialized=serialize_example,
                                             features={
                                                 # 'label': tf.FixedLenFeature([VOCUBLARY_SIZE], tf.int64),
                                                 'label': tf.VarLenFeature(dtype=tf.int64),
                                                 'image': tf.FixedLenFeature([], tf.string),
                                                 'h': tf.FixedLenFeature([], tf.int64),
                                                 'w': tf.FixedLenFeature([], tf.int64),
                                                 'c': tf.FixedLenFeature([], tf.int64)
                                             })
    h = tf.cast(image_features['h'], tf.int32)
    w = tf.cast(image_features['w'], tf.int32)
    c = tf.cast(image_features['c'], tf.int32)

    image = tf.decode_raw(image_features['image'], tf.uint8)
    image = tf.cast(image, tf.float32)
    image = tf.reshape(image, shape=[h, w, c])
    resized_image = tf.image.resize_image_with_crop_or_pad(image, target_height=32, target_width=max_width)
    resized_image = tf.reshape(resized_image, shape=[32, max_width, 3])

    label = tf.cast(image_features['label'], tf.int32)

    min_fraction_of_example_in_queue = 0.4
    if train is True:
        min_queue_examples = int(min_fraction_of_example_in_queue * NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN)
        train_image_batch, train_label_batch = tf.train.shuffle_batch([resized_image, label],
                                                                      batch_size=batch_size,
                                                                      capacity=min_queue_examples + 3 * batch_size,
                                                                      min_after_dequeue=min_queue_examples,
                                                                      num_threads=32)
        return train_image_batch, train_label_batch
    else:
        min_queue_examples = int(min_fraction_of_example_in_queue * NUM_EXAMPLES_PER_EPOCH_FOR_TEST)
        test_image_batch, test_label_batch = tf.train.batch([resized_image, label],
                                                            batch_size=batch_size,
                                                            capacity=min_queue_examples + 3 * batch_size,
                                                            num_threads=32)
        return test_image_batch, test_label_batch


def index_to_word(result):
    return ''.join([CHAR_VECTOR[i] for i in result])


def main(argv):
    generation_TFRecord('./dataset/images')
    train_image, train_label = read_tfrecord('./dataset/train_dataset.tfrecords', 250, 32)
    test_image, test_label = read_tfrecord('./dataset/test_dataset.tfrecords', 250, 32)
    with tf.Session() as session:
        session.run(tf.group(tf.global_variables_initializer(),
                             tf.local_variables_initializer()))
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(coord=coord)

        image_train, label_train = session.run([train_image, train_label])
        print(image_train.shape)

        image_test, label_test = session.run([test_image, test_label])
        print(image_test.shape)

        for image, label in  zip(image_test, label_test):
            # 将array转换成image
            img = Image.fromarray(image, 'RGB')
            img.save(index_to_word(label) + '.jpg')
            print(index_to_word(label))

        coord.request_stop()
        coord.join(threads=threads)


if __name__ == '__main__':
    tf.app.run()

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
### 回答1: 好的,以下是用 PyQt5 实现读取文件读取 `ui` 文件分离的代码示例: ``` import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QTabWidget, QTextEdit, QFileDialog from PyQt5.uic import loadUi class MainWindow(QMainWindow): def __init__(self): super().__init__() loadUi("main_window.ui", self) self.tab_widget = self.findChild(QTabWidget, "tabWidget") self.tab_widget.currentChanged.connect(self.tab_changed) def tab_changed(self, index): current_tab = self.tab_widget.widget(index) try: # Check if the file has already been loaded getattr(current_tab, "file_loaded") except AttributeError: # Load the file file_name, _ = QFileDialog.getOpenFileName() with open(file_name, "r") as file: current_tab.file_loaded = True current_tab.setPlainText(file.read()) if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) ``` 在上面的代码中,我们使用了 PyQt5 内置的 `loadUi` 函数来加载外部的 `ui` 文件,并通过 `findChild` 函数来获取 `QTabWidget` 对象。每当选项卡改变时,我们会在 `tab_changed` 函数中检查该选项卡中的文件是否已经被加载,如果没有被加载,我们就使用 `QFileDialog` 来获取文件名,并将文件内容读入到当前选项卡中。 ### 回答2: 在pyqt5中,可以使用QTabWidget来创建一个选项卡窗口,然后可以通过读取文件读取ui文件的方式来实现选项卡的分离。 首先,我们需要导入相关的库和模块: ``` from PyQt5.QtWidgets import QApplication, QMainWindow, QTabWidget, QWidget from PyQt5.QtUiTools import QUiLoader from PyQt5.QtCore import QFile ``` 接下来,我们创建一个主窗口类,继承自QMainWindow: ``` class MainWindow(QMainWindow): def __init__(self): super().__init__() # 创建一个QTabWidget self.tab_widget = QTabWidget(self) self.setCentralWidget(self.tab_widget) # 调用读取文件读取ui文件来创建选项卡 self.load_file_tab() self.load_ui_tab() def load_file_tab(self): # 创建一个QWidget来作为选项卡的内容 file_tab = QWidget() # 读取文件的逻辑代码 file_label = QLabel('读取文件', file_tab) # 将QWidget添加到QTabWidget中 self.tab_widget.addTab(file_tab, '文件选项卡') def load_ui_tab(self): # 创建一个QWidget来作为选项卡的内容 ui_tab = QWidget() # 读取ui文件的逻辑代码 loader = QUiLoader() ui_file = QFile('ui_file.ui') ui_file.open(QFile.ReadOnly) ui_widget = loader.load(ui_file) ui_file.close() # 将读取到的ui文件内容添加到QWidget中 layout = QVBoxLayout() layout.addWidget(ui_widget) ui_tab.setLayout(layout) # 将QWidget添加到QTabWidget中 self.tab_widget.addTab(ui_tab, 'UI选项卡') ``` 在上述代码中,我们通过在加载选项卡内容时调用`load_file_tab`和`load_ui_tab`函数来分别实现读取文件读取ui文件的逻辑,并将其添加到QTabWidget中。 最后,我们可以创建一个QApplication实例,然后创建一个MainWindow实例,并调用`show`函数来显示主窗口: ``` if __name__ == '__main__': app = QApplication([]) window = MainWindow() window.show() app.exec_() ``` 通过这样的方式,我们就成功实现了在pyqt5中利用QTabWidget来实现读取文件读取ui文件的分离。 ### 回答3: 在 PyQt5 中,我们可以使用 QTabWidget 控件来实现标签页功能。在这个问题中,要实现 QTabWidget 读取文件读取 UI 文件分离的功能,可以分为以下两个步骤: 1. 读取文件: 首先,我们需要通过 QFileDialog 来选择要读取文件,然后读取文件的内容,并将内容显示在 QTabWidget 的标签页中。 ```python import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QTabWidget, QTextEdit, QFileDialog class MyTabWidget(QTabWidget): def __init__(self): super().__init__() self.addTab(QTextEdit(), "Tab 1") # 默认添加一个标签页 self.addTab(QTextEdit(), "Tab 2") # 默认添加一个标签页 self.addTab(QTextEdit(), "Tab 3") # 默认添加一个标签页 self.addTab(QTextEdit(), "Tab 4") # 默认添加一个标签页 def open_file(self): file_dialog = QFileDialog() file_name = file_dialog.getOpenFileName() # 获取选择的文件名 with open(file_name[0], 'r') as file: content = file.read() # 读取文件内容 current_index = self.currentIndex() # 获取当前选中的标签页索引 self.widget(current_index).setPlainText(content) # 将文件内容显示在当前标签页的 QTextEdit 控件中 class MyMainWindow(QMainWindow): def __init__(self): super().__init__() self.tab_widget = MyTabWidget() self.setCentralWidget(self.tab_widget) self.statusBar().showMessage("Ready") def open_file(self): self.tab_widget.open_file() if __name__ == '__main__': app = QApplication(sys.argv) main_win = MyMainWindow() main_win.show() sys.exit(app.exec_()) ``` 2. 读取 UI 文件: 我们可以将读取 UI 文件的功能封装成一个类,然后在我们的主窗口中调用该类的函数,来将 UI 文件的内容显示在 QTabWidget 的标签页中。 ```python import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QTabWidget, QTextEdit, QFileDialog, QWidget, QVBoxLayout from PyQt5.QtUiTools import QUiLoader class MyTabWidget(QTabWidget): def __init__(self): super().__init__() self.addTab(QTextEdit(), "Tab 1") # 默认添加一个标签页 self.addTab(QTextEdit(), "Tab 2") # 默认添加一个标签页 self.addTab(QTextEdit(), "Tab 3") # 默认添加一个标签页 self.addTab(QTextEdit(), "Tab 4") # 默认添加一个标签页 def open_ui_file(self): file_dialog = QFileDialog() file_name = file_dialog.getOpenFileName() # 获取选择的文件名 ui_file = file_name[0] widget = QUiLoader().load(ui_file) # 从 UI 文件中加载界面 current_index = self.currentIndex() # 获取当前选中的标签页索引 # 将加载的界面添加到当前标签页中 self.widget(current_index).setLayout(QVBoxLayout()) self.widget(current_index).layout().addWidget(widget) class MyMainWindow(QMainWindow): def __init__(self): super().__init__() self.tab_widget = MyTabWidget() self.setCentralWidget(self.tab_widget) self.statusBar().showMessage("Ready") def open_file(self): self.tab_widget.open_ui_file() if __name__ == '__main__': app = QApplication(sys.argv) main_win = MyMainWindow() main_win.show() sys.exit(app.exec_()) ``` 以上是一个简单的示例,演示了如何实现 QTabWidget 读取文件读取 UI 文件分离的功能。在这个示例中,默认添加了 4 个标签页,通过打开文件或者 UI 文件选择对应的文件后,将其内容显示在当前标签页中的 QTextEdit 或者加载的界面中。请根据具体需求进行适当修改和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马鹤宁

谢谢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值