您的位置:首页 > 其它

tensorflow学习笔记2——TFrecord

2018-08-31 01:13 260 查看

       既然是大数据,那么处理数据肯定是第一步。报了竞赛拿到了图片资源却一时不知道怎么下手,想起以前解除过 PIL,正可以用来处理图像。查了一下发现 tensorflow 自带了 TFrecord 来处理数据。

        我想处理的图像集是汉字书法,一共一百个字即存放在一百个文件夹中,每个文件夹中有这个字不同字体的 400 个图片,而文件夹的名字也正是字体的属性——名字,我们需要试着将其中一个文件夹中的内容写成 TFrecord 数据。

       对文件夹的操作是用到 os 库中的 os.listdir(path) 方法,返回的是一个列表,储存了该文件夹中的各个文件。想着 print 出来看,没想到提示 [Decode error - output not utf-8],似乎是 print 不支持输出中文。百度了一番采纳了https://www.geek-share.com/detail/2638966582.html 中的第二种方法,在系统变量中添加一个 PYTHONIOENCODING,值为 utf-8,重启 sublime text 就可以用了。

       在网上看了好几篇文章大家的思路都差不多,大同小异。我采取了从中的一篇稍微修改了一下:

[code]from PIL import Image
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

IMAGE_PATH = "D:/python3.6/data/"
tfrecord_file = IMAGE_PATH + "test.tfrecord"

def _int64_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]))

path = "D:/BaiduNetdiskDownload/TMD/train/train/"
dir = os.listdir(path)

new_path = path + dir[0]
new_dir = os.listdir(new_path)
label = bytes(dir[0],encoding = "utf-8")

writer = tf.python_io.TFRecordWriter(tfrecord_file)

n_new_path = new_path + "/" + new_dir[0]
im = Image.open(n_new_path)

im_s = im.tobytes()
print(type(im_s))
example = tf.train.Example(features=tf.train.Features(feature={
"label": _int64_feature(0),
"image": _bytes_feature(im_s)
}))
writer.write(example.SerializeToString())
writer.close()

reader = tf.TFRecordReader()

fliename_queue = tf.train.string_input_producer(["D:/python3.6/data/test.tfrecord"])
_, serialized_example = reader.read(fliename_queue)

features = tf.parse_single_example(
serialized_example,
features = {
"image": tf.FixedLenFeature([],tf.string),
"label": tf.FixedLenFeature([],tf.int64)
})

image = tf.decode_raw(features["image"], tf.uint8)
label = tf.cast(features["label"], tf.int32)

with tf.Session() as sess:

coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)

label,image = sess.run([label,image])

image = np.fromstring(image, dtype=np.unit8)
image = tf.reshape(image,[161,139,1])
image = tf.image.convert_image_dtype(image, dtype=tf.uint8)
image = tf.image.encode_jpeg(image)

with tf.gfile.GFile("D:/python3.6/data/pic.jpg", "wb") as f:
f.write(sess.run(image))

       运行过程没出现什么错误的样子,但是最后生成的图像完全失真,应该是中间各种编码转码出现了错误。整理一下代码段的逻辑:

       首先一张 JPEG 格式的图片,经过 PIL 变成 Image 实例对象,也可以理解成是一个二维数组,二维因为我们是灰度信息图,所用的图片像素为 161x139,再通过 tobytes() 方法转化为二进制码,转化的结果是 Bytes 对象,可以输出来看,是一连串的 \x9e\x13 之类的十六进制码。这串十六进制码就可以直接传递给 tf.train.examle() 去创建一个实例,然后再写入 TFrecord 文件中。之后再取出来,通过创建一个 reader 来打开 TFrecord 文件,取出我们的 image 和 label,这时得到的 image 是一个一维列表,列表的长度就是图片长宽的乘积即像素点个数;然后在转码之后去做了一个 np.fromstring操作,哎?!不对呀 image 对象是一个数组不是一个字符串怎么可以由这个方法呢?又找了两篇文章,看到有的博主用的是 fromarray 方法,我便跟着改过来,参数 dtype=np.uint8 表示这是 8 位无符号整型数据,取值从 0 到 255,正符合我们每个像素点的值都是 0 到 255 的取值。得到之后对结构进行调整,把一维数组 reshape 成二维数组,然后再把二维数组给丢进去 encode_jpeg 里面生成一张图片。隐约之中感觉对图像的理解有更深刻了一丢丢。

        不过改过来之后仍然报错,无可奈何之余想去看看我这张图片长啥样,点进去属性发现居然是宽度161高度139,这跟我从 img.size 里面看到的不一致啊!!原来,我们 Image 的 size 属性得到的两个数值第一个表示宽度第二个表示高度。而在 tensorflow 中第一个参数还是表示高度第二个才表示宽度,这也就解释了为何图片会被失真。于是重新把代码修改了一下:

[code]from PIL import Image
import tensorflow as tf
import numpy as np
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

IMAGE_PATH = "D:/python3.6/data/"
tfrecord_file = IMAGE_PATH + "test.tfrecord"

def _int64_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]))

path = "D:/BaiduNetdiskDownload/TMD/train/train/"
dir = os.listdir(path)

new_path = path + dir[0]
new_dir = os.listdir(new_path)
label = bytes(dir[0],encoding = "utf-8")
writer = tf.python_io.TFRecordWriter(tfrecord_file)
print(new_dir[0])

n_new_path = new_path + "/" + new_dir[0]
im = Image.open(n_new_path)

print(im.size)#得到(161,139),即宽度161高度139

im_s = im.tobytes()

example = tf.train.Example(features=tf.train.Features(feature={
"label": _int64_feature(12),
"image": _bytes_feature(im_s)
}))
writer.write(example.SerializeToString())
writer.close()

reader = tf.TFRecordReader()

fliename_queue = tf.train.string_input_producer(["D:/python3.6/data/test.tfrecord"])
_, serialized_example = reader.read(fliename_queue)
print(serialized_example)
features = tf.parse_single_example(
serialized_example,
features = {
"image": tf.FixedLenFeature([],tf.string),
"label": tf.FixedLenFeature([],tf.int64)
})

image = tf.decode_raw(features["image"], tf.uint8)
label = tf.cast(features["label"], tf.int32)
image = tf.reshape(image,[139,161])

print(image ,label)

with tf.Session() as sess:

coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)

label,image = sess.run([label,image])

image = Image.fromarray(image.astype("uint8"))
image = tf.reshape(image,[139,161,1])
image = tf.image.encode_jpeg(image)

with tf.gfile.GFile("D:/python3.6/data/pic.jpg", "wb") as f:
f.write(sess.run(image))

        至此完成了单张 Image 转成 TFrecord 格式以及 TFrecord 格式转 Image 图片的操作。如果是多张图片,就是在外面嵌套一层循环,把每次图片经 tobytes 转码产生的内容和标签都存入同一个 TFrecord 文件中,全部完成再 writer.close() 关闭输入器。去除图片的话就要根据前面嵌套的层数,每次执行 sess.run([label, image]) 就是在队列中进行操作,取出一个图片和他对应的标签值。

------------------------------------------------------------------------2018.09.08---------------------------------------------------------------------------------

       后面发现,我们用生成的 TFrecord 文件尝试运行的时候出了问题,是在计算交叉熵的时候维度不对应。因为我们的 CNN 网络对输入的一张图片的像素信息,最后返回的 OUTPUT_NODE 是一个 (100, ) 的数组,而我们在生成 TFrecord 的时候的 label 却只是个数字。所以,把我们原先一个数字改成一个数组,语句如下:

[code]......
lbl = np.array([int(x == i) for x in range(100)])
lbl = lbl.tobytes()

example = tf.train.Example(features=tf.train.Features(feature={
"label": _bytes_feature(lbl),
"image": _bytes_feature(im_s)
}))
......

--------------------------------------------------------------------------2018.09.09-------------------------------------------------------------------------------

       做出如上更改之后,在打开 TFrecord 文件并把数据传递给 shuffle_batch 的时候会报错,报错的内容是ValueError: All shapes must be fully defined: [TensorShape([Dimension(200), Dimension(200), Dimension(1)]), TensorShape([Dimension(None)])]。这个错误的意思就是,我们在传给 shuffle_batch 的两个元组中,第一个有明显的将图片像素重新转为二维结构,即有经过了 tf.image.resize_image 的处理;而第二维中,labels 标签没有经过这个步骤,所有在识别的时候会显示 Dimension(None),而问题不在于说给 label 补上上述步骤,而是标签仍然要以一个整数的形式存储,可以在后续的时候 shuffle_batch 中再换种形式。虽然会在另外的文章里讲到如何在 shuffle_batch 函数中处理,不过还是把完整的更新的代码段写出来为好,以免自己看了都不知所以然。

       因为我们数据集里面的图片大小不一,所以把长宽也当成特征输入进去,在后面解读的时候还需要这两个数字来帮助我们把图片像素恢复成二维。

[code]from PIL import Image
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

IMAGE_PATH = "D:/python3.6/data/"
tfrecord_file = IMAGE_PATH + "train_set.tfrecord"

def _int64_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]))

path = "D:/BaiduNetdiskDownload/TMD/train/train/"
dir = os.listdir(path)

writer = tf.python_io.TFRecordWriter(tfrecord_file)

for i in range(len(dir)):

new_path = path + dir[i]
new_dir = os.listdir(new_path)
print("Working on the %s class"%i)
for j in range(len(new_dir)):

n_new_path = new_path + "/" + new_dir[j]
im = Image.open(n_new_path)
width, height= im.size

im_s = im.tobytes()

example = tf.train.Example(features=tf.train.Features(feature={
"label": _int64_feature(i),
"image": _bytes_feature(im_s),
"width": _int64_feature(width),
"height": _int64_feature(height)
}))
writer.write(example.SerializeToString())

writer.close()

 

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐