Claudia P
Claudia P

Reputation: 1

how to create a dataset with a small fraction of all the images with Tensorflow

I have 99 thousand images in a folder, but I only need 10000 for my dataset. I have tried randomly choosing the images first and then creating the dataset, but it doesn't seem to work. I need to use Tensorlfow for this.

I have been stuck with this problem for over a week. If anyone has another way to create a dataset wit only a fraction of the total number of images that is completely different from mine, that's okay with me.

Thank you!

Right now I am trying to make the following code work:

import tensorflow as tf
import numpy as np
import os

# Define the directory where your images are located
data_dir = '/home/clau/Escritorio/cropped/'

# Load the list of image filenames
filenames = tf.constant(os.listdir(data_dir))
num_images = len(filenames)

# Shuffle the filenames
filenames = tf.random.shuffle(filenames)

# Select the first 12000 filenames
filenames = filenames[:10000]


# Define a function to load and preprocess each image
def load_and_preprocess_image(filename):
    # Convert the filename tensor to a string
    filename = filename.numpy().decode('utf-8')

    # Load the image file
    file_path = os.path.join(data_dir, filename)
    img = tf.io.read_file(file_path)

    # Decode the image and convert to floating-point tensor
    img = tf.image.decode_jpeg(img, channels=3)
    img = tf.image.convert_image_dtype(img, tf.float32)

    # Resize the image to 256x256
    img = tf.image.resize(img, [256, 256])

    # Normalize the pixel values to the range [0,1]
    img = img / 255.0

    return img

# Create an empty dataset
dataset = tf.data.Dataset.from_tensor_slices(tf.constant([], dtype=tf.string))

for imgfile in filenames:
    processed_img = load_and_preprocess_image(imgfile)
    
    # Add the image
    dataset = dataset.concatenate(tf.data.Dataset.from_tensor_slices(processed_img))


it gives my the following error:

TypeError: Incompatible dataset elements: TensorSpec(shape=(), dtype=tf.string, name=None) vs. TensorSpec(shape=(256, 3), dtype=tf.float32, name=None)

Upvotes: 0

Views: 250

Answers (1)

Keerthitheja S.C.
Keerthitheja S.C.

Reputation: 167

You can create Tensorflow records with the specific number of images you want and parse them before you train your model.

Here is a sample code to create the TF records file and parse them to your model. I am assuming that the image file names are your labels which are integers since its not clear from your code what labels you are using.

import tensorflow as tf
import numpy as np
import random
import os

def image_feature(value):
    """
    Returns a bytes_list from a string / byte.
    """

    return tf.train.Feature(
        bytes_list=tf.train.BytesList(value=[tf.io.encode_jpeg(value).numpy()])
    )


def bytes_feature(value):
    """Returns a bytes_list from a string / byte."""
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))


def float_feature(value):
    """Returns a float_list from a float / double."""
    return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))


def int64_feature(value):
    """Returns an int64_list from a bool / enum / int / uint."""
    return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))

def create_example(image:np.ndarray, label:int):
    feature = {
        "label" : int64_feature(label),
        "image" : image_feature(image),        
    }
    return tf.train.Example(features=tf.train.Features(feature=feature))

path_to_your_images = "/path/to/your/images"

#Here we sample 10000 random images from your image folder
images_path_list = random.sample(os.listdir(path_to_your_images), 10000)

with tf.io.TFRecordWriter("train.tfrec") as writer:
    for file_path in images_path_list:
        img = tf.io.read_file(os.path.join(path_to_your_images, file_path))
        label = int(file_path.strip(".jpg"))
        """ 
        Here we decode the image bytes first and encode it again using encode_jpeg(). 
        This encoding reduces the memory occupied by the TF records file. 
        You can also directly write the raw image bytes to TF records but that would make 
        the file size of TF records to be huge as it saves the actual bytes of the image
        """
        example = create_example(tf.image.decode_jpeg(img, channels=3), label) 
        writer.write(example.SerializeToString())

The above code will create TF records file with 10000 images. Now you can use the below code to parse the images and labels and create your dataset handle to train your models

def _parse_dataset(example_proto):
    _feature_description = {
        "image" : tf.io.FixedLenFeature([], tf.string),        
        "label" : tf.io.FixedLenFeature([], tf.int64)
    }
    
    example = tf.io.parse_single_example(example_proto, _feature_description)

    image = tf.io.decode_jpeg(example["image"])    
    
    image = tf.cast(image, tf.float32) / 255.0
    label = example["label"]

def create_dataset(tfrecord_paths, batch_size):
    raw_dataset = tf.data.TFRecordDataset(tfrecord_paths)
    dataset = raw_dataset.map(_parse_dataset)
   
    dataset = dataset.batch(batch_size)
    return dataset


train_dataset = create_dataset("/path/to/train.tfrec",8)

Now you can directly use train_dataset in your model.fit(train_dataset) to train your model. To access one single example from the dataset you can do train_dataset.take(1). This will give you two values one is your image of shape (batch_size, height, width, channels) and other is your label

Note: You are not limited to only your image and labels, you can also write other information into TF records file via the feature_description dictionary. For example, if you want to add the image file name, width and height of your image for every example, then you can modify your feature description as follows

feature = {
        "file_name" : bytes_feature(filename.encode("utf8")),
        "width" : int64_feature(width),
        "height" : int64_feature(height)
        "label" : int64_feature(label),
        "image" : image_feature(image),        
    }

You need to make sure to use the same feature description while parsing your dataset.

I hope this helps. Cheers !!!

Edit: To do the same without using TF Records, you can do as follows


num_images = 10000

path_to_your_images = "/path/to/your/images"

all_image_paths = [os.path.join(path_to_your_images, file) for file in os.listdir(path_to_your_images)]

np.random.shuffle(all_image_paths)

image_path_list = all_image_paths[:num_images]

def process_images(image_file_path):

    image = tf.io.read_file(image_file_path)
    image = tf.image.decode_jpeg(image, channels=3)
    image = tf.image.resize(image,(resize_height, resize_width))
    image = tf.cast(image, tf.float32) / 255.0
    return image

image_dataset = tf.data.Dataset.from_tensor_slices(image_path_list)

image_dataset = image_dataset.map(process_images)

labels = np.zeros(num_images)

label_dataset = tf.data.Dataset.from_tensor_slices(labels)

dataset = tf.data.Dataset.zip((image_dataset, label_dataset))

dataset = dataset.shuffle(num_images).batch(8)

Now you can use dataset to train your model

Upvotes: 0

Related Questions