使用tf.data.experimental.make_csv_dataset或read_csv选项预处理图像

时间:2018-12-20 19:08:07

标签: tensorflow tensorflow-datasets tensorflow-estimator

我正在添加我的问题的摘要,以使其更易于理解: 我想确切地执行以下tensorflow示例中的操作: https://www.tensorflow.org/guide/datasets

# Reads an image from a file, decodes it into a dense tensor, and resizes it
# to a fixed shape.
def _parse_function(filename, label):
  image_string = tf.read_file(filename)
  image_decoded = tf.image.decode_jpeg(image_string)
  image_resized = tf.image.resize_images(image_decoded, [28, 28])
  return image_resized, label

# A vector of filenames.
filenames = tf.constant(["/var/data/image1.jpg", "/var/data/image2.jpg", ...])

# `labels[i]` is the label for the image in `filenames[i].
labels = tf.constant([0, 37, ...])

dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
dataset = dataset.map(_parse_function)

唯一的区别是:我从具有更多功能的CSV读取数据,然后调用map方法:

dataset = tf.data.experimental.make_csv_dataset(file_pattern=CSV_PATH_TRAIN,
                                                    batch_size=2,
                                                    header=True,
                                                    label_name = 'label').map(_parse_function) 

我的_parse_function看起来如何?如何访问图像路径功能,将其更新为图像表示形式,并返回图像的修改后的数字矩阵功能,而无需更改其他功能?

谢谢, 埃兰语

===================这是我的代码尝试:==================

我的代码读取带有功能列和标签的CSV。功能之一是图像路径,其他功能是字符串。 图像路径需要处理成图像编号矩阵。 我尝试使用以下选项进行操作。两种方式下,tf.read_file都会失败,并输入尺寸错误。 我的问题是如何一次将一张图像传递到map方法中

def read_image_png_option_1(image_path, depth=3, scale=False):
  """Reads the image from image_path (tf.string tensor) [jpg image].
  Cast the result to float32 and if scale=True scale it in [-1,1]
  using scale_image. Otherwise the values are in [0,1]
  Reuturn:
      the decoded jpeg image, casted to float32
  """
  image = tf.image.convert_image_dtype(
      tf.image.decode_png(tf.read_file(image_path), channels=depth),
      dtype=tf.float32)
  if scale:
      image = scale_image(image)
  return image

def read_image_png_option_2(features, depth=3, scale=False):
  """Reads the image from image_path (tf.string tensor) [jpg image].
  Cast the result to float32 and if scale=True scale it in [-1,1]
  using scale_image. Otherwise the values are in [0,1]
  Reuturn:
      the decoded jpeg image, casted to float32
  """
  image = tf.image.convert_image_dtype(
      tf.image.decode_png(tf.read_file(features['image']), channels=depth),
      dtype=tf.float32)
  if scale:
      image = scale_image(image)
  features['image'] = image
  return features

def make_input_fn(fileName,batch_size=8, perform_shuffle=True):
  """An input function for training """
  def _input_fn():
    def  decode_csv(line):
      print('line is ',line)
      filename_col,label_col,gender_col,ethinicity = tf.decode_csv(line, 
                                                                   [[""]]*amount_of_columns_csv, 
                                                                   field_delim=",",
                                                                   na_value='NA',
                                                                   select_cols=None)
      image_col = read_image_png_option_1(filename_col)
      d = dict(zip(['image','label','gender','ethinicity'], [image_col,label_col,gender_col,ethinicity])), label
      return d

    ## OPTION 1:
#     filenames could be more than one
#    dataset = tf.data.TextLineDataset(filenames=fileName).skip(1).batch(batch_size).map(decode_csv)

    ## OPTION 2: 
    dataset = tf.data.experimental.make_csv_dataset(file_pattern=CSV_PATH_TRAIN,
                                                    batch_size=2,
                                                    header=True,
                                                    label_name = 'label').map(read_image_png_option_2)
                                                    #select_columns=[0,1]) #[tf.string,tf.string,tf.string,tf.string])

    if perform_shuffle:
        dataset = dataset.shuffle(buffer_size=256)
    return dataset
  return _input_fn()

train_input_fn = lambda: make_input_fn(CSV_PATH_TRAIN)
train_spec = tf.estimator.TrainSpec(input_fn=train_input_fn, max_steps=50)

eval_input_fn = lambda: make_input_fn(CSV_PATH_VAL)
eval_spec = tf.estimator.EvalSpec(eval_input_fn)

feature_columns = [tf.feature_column.numeric_column("image",shape=(224,224)),  # here i need a pyhton method to transform
                   tf.feature_column.categorical_column_with_vocabulary_list("gender", ["ww","ee"]),
                   tf.feature_column.categorical_column_with_vocabulary_list("ethinicity",["xx","yy"])]

estimator = tf.estimator.DNNClassifier(feature_columns=feature_columns,hidden_units=[1024, 512, 256],warm_start_from=ws)

tf.estimator.train_and_evaluate(estimator, train_spec=train_spec, eval_spec=eval_spec)

选项2的错误: ValueError:形状必须为0级,但输入形状为[2]的“ ReadFile”(op:“ ReadFile”)为1级。

选项1的错误: ValueError:形状必须为0级,但输入形状为[?]的“ ReadFile”(op:“ ReadFile”)为1级。

感谢您的帮助。 谢谢

1 个答案:

答案 0 :(得分:0)

首先,您需要将CSV文件读入数据集。

然后,您可以为CSV中的每一行调用解析函数。

def getInput(fileList):
    # returns a dataset containing list of filenames
    files = tf.data.Dataset.from_tensor_slices(fileList)

    # Returs a dataset containing list of rows taken from all the files in file list.
    # dataset is filled dynamically and not all entries are read at once
    dataset = files.interleave(tf.data.TextLineDataset)

    # call parse function for each row
    # returned dataset will contain list of whatever the parse function is returning for the row
    # we want the image path to be converted to decoded image in parse function
    dataset = dataset.map(_parse_function, num_parallel_calls=8)
    # return an iterator for the dataset which will be used to get elements.
    return dataset.make_one_shot_iterator().get_next()

解析函数将仅传递一个参数,该参数将是CSV文件中的一行。您需要解码CSV并对每个值进行进一步处理。

假设您的CSV文件中有3列,每个列都是一个字符串。

def _parse_function(value):
    columns_default = [[""], [""], [""]]
    # this will be a tensor of columns in the row
    columns = tf.decode_csv(value, record_defaults=columns_default,
            field_delim=',')
    col_names = ["label", "imagepath", "c3"]
    features = dict(zip(col_names, columns))
    for f, tensor in features.items():
        # process imagepath to decoded image
        if f == "imagepath":
            image_string = tf.read_file(tensor)
            image_decoded = tf.image.decode_jpeg(image_string)
            image_resized = tf.image.resize_images(image_decoded, [28, 28])
            features[f] = image_resized
    labels = tf.equal(features.pop('label'), "1")
    labels = tf.expand_dims(labels, 0)
    return features, labels

编辑:

注释说明:

  1. 数据集对象仅包含元素列表。元素可以是张量或张量的元组等。Tensor对象可以包含任何东西。它可以表示单个功能,单个记录或一批记录。进一步的数据集API提供了方便的方法来操作其中的元素。
    如果您将数据集与另一个API(例如estimator)一起使用,则他们希望数据集元素采用特定格式,这是需要从我们的输入函数返回的格式,例如。

https://www.tensorflow.org/api_docs/python/tf/estimator/Estimator#train

我已经在上面的代码块中进行了编辑,以描述每个步骤将包含的数据集对象。

  1. 据我了解,您将图像路径作为CSV中的字段之一,并且想要将该路径转换为实际的解码图像,并将其用作功能之一。

    由于图像将只是功能之一,因此您不应尝试仅使用图像文件来创建数据集。数据集对象将一次包含您的所有功能。

所以这样做是不正确的:

files = tf.data.Dataset.from_tensor_slices(ds['imagepath']) 
dataset = files.interleave(tf.data.TextLineDataset)

如果您使用make_csv()函数读取csv,则它将csv的每一行转换为一条记录,其中一条记录将包含所有功能的列表,与csv的列相同。

因此返回的数据集中的每个元素应包含一个包含所有特征的张量。

在这里,您的图像路径将是功能之一。现在您想将该图像路径转换为解码图像。

我想您可以通过使用map()函数对数据集的元素应用parse函数来做到这一点,但这会有些棘手,因为所有功能都已经打包在一个张量中。