├── .gitignore
├── README.md
├── cat.jpg
├── dataset.py
├── dog.jpg
├── tf1_cnn.ipynb
├── tf1_requirements.txt
├── tf2_cnn.ipynb
└── tf2_requirements.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | data/
2 | .ipynb_checkpoints/
3 | __pycache__/
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tensorflow Image Classification
2 | CNN for multi-class image recognition in tensorflow
3 |
4 | Notebook converted from Hvass-Labs' tutorial in order to work with custom datasets, flexible image dimensions, 3-channel images, training over epochs, early stopping, and a deeper network. An updated version of the notebook for TensorFlow 2 is also included, along with a separate requirements file for that TensorFlow version.
5 |
6 | This example uses Kaggle's cats vs. dogs dataset.
7 |
--------------------------------------------------------------------------------
/cat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rdcolema/tensorflow-image-classification/d9ca52018208a9cfbb7c74b95fab9d36d3f36d7a/cat.jpg
--------------------------------------------------------------------------------
/dataset.py:
--------------------------------------------------------------------------------
1 | import os
2 | import glob
3 | import numpy as np
4 | import cv2
5 | from sklearn.utils import shuffle
6 |
7 |
8 | def load_train(train_path, image_size, classes):
9 | images = []
10 | labels = []
11 | ids = []
12 | cls = []
13 |
14 | print('Reading training images')
15 | for fld in classes: # assuming data directory has a separate folder for each class, and that each folder is named after the class
16 | index = classes.index(fld)
17 | print('Loading {} files (Index: {})'.format(fld, index))
18 | path = os.path.join(train_path, fld, '*g')
19 | files = glob.glob(path)
20 | for fl in files:
21 | image = cv2.imread(fl)
22 | image = cv2.resize(image, (image_size, image_size), cv2.INTER_LINEAR)
23 | images.append(image)
24 | label = np.zeros(len(classes))
25 | label[index] = 1.0
26 | labels.append(label)
27 | flbase = os.path.basename(fl)
28 | ids.append(flbase)
29 | cls.append(fld)
30 | images = np.array(images)
31 | labels = np.array(labels)
32 | ids = np.array(ids)
33 | cls = np.array(cls)
34 |
35 | return images, labels, ids, cls
36 |
37 |
38 | def load_test(test_path, image_size):
39 | path = os.path.join(test_path, '*g')
40 | files = sorted(glob.glob(path))
41 |
42 | X_test = []
43 | X_test_id = []
44 | print("Reading test images")
45 | for fl in files:
46 | flbase = os.path.basename(fl)
47 | img = cv2.imread(fl)
48 | img = cv2.resize(img, (image_size, image_size), cv2.INTER_LINEAR)
49 | X_test.append(img)
50 | X_test_id.append(flbase)
51 |
52 | ### because we're not creating a DataSet object for the test images, normalization happens here
53 | X_test = np.array(X_test, dtype=np.uint8)
54 | X_test = X_test.astype('float32')
55 | X_test = X_test / 255
56 |
57 | return X_test, X_test_id
58 |
59 |
60 | class DataSet(object):
61 |
62 | def __init__(self, images, labels, ids, cls):
63 | """Construct a DataSet. one_hot arg is used only if fake_data is true."""
64 |
65 | self._num_examples = images.shape[0]
66 |
67 | # Convert shape from [num examples, rows, columns, depth]
68 | # to [num examples, rows*columns] (assuming depth == 1)
69 | # Convert from [0, 255] -> [0.0, 1.0].
70 |
71 | images = images.astype(np.float32)
72 | images = np.multiply(images, 1.0 / 255.0)
73 |
74 | self._images = images
75 | self._labels = labels
76 | self._ids = ids
77 | self._cls = cls
78 | self._epochs_completed = 0
79 | self._index_in_epoch = 0
80 |
81 | @property
82 | def images(self):
83 | return self._images
84 |
85 | @property
86 | def labels(self):
87 | return self._labels
88 |
89 | @property
90 | def ids(self):
91 | return self._ids
92 |
93 | @property
94 | def cls(self):
95 | return self._cls
96 |
97 | @property
98 | def num_examples(self):
99 | return self._num_examples
100 |
101 | @property
102 | def epochs_completed(self):
103 | return self._epochs_completed
104 |
105 | def next_batch(self, batch_size):
106 | """Return the next `batch_size` examples from this data set."""
107 | start = self._index_in_epoch
108 | self._index_in_epoch += batch_size
109 |
110 | if self._index_in_epoch > self._num_examples:
111 | # Finished epoch
112 | self._epochs_completed += 1
113 |
114 | # # Shuffle the data (maybe)
115 | # perm = np.arange(self._num_examples)
116 | # np.random.shuffle(perm)
117 | # self._images = self._images[perm]
118 | # self._labels = self._labels[perm]
119 | # Start next epoch
120 |
121 | start = 0
122 | self._index_in_epoch = batch_size
123 | assert batch_size <= self._num_examples
124 | end = self._index_in_epoch
125 |
126 | return self._images[start:end], self._labels[start:end], self._ids[start:end], self._cls[start:end]
127 |
128 |
129 | def read_train_sets(train_path, image_size, classes, validation_size=0):
130 | class DataSets(object):
131 | pass
132 |
133 | data_sets = DataSets()
134 |
135 | images, labels, ids, cls = load_train(train_path, image_size, classes)
136 | images, labels, ids, cls = shuffle(images, labels, ids, cls) # shuffle the data
137 |
138 | if isinstance(validation_size, float):
139 | validation_size = int(validation_size * images.shape[0])
140 |
141 | validation_images = images[:validation_size]
142 | validation_labels = labels[:validation_size]
143 | validation_ids = ids[:validation_size]
144 | validation_cls = cls[:validation_size]
145 |
146 | train_images = images[validation_size:]
147 | train_labels = labels[validation_size:]
148 | train_ids = ids[validation_size:]
149 | train_cls = cls[validation_size:]
150 |
151 | data_sets.train = DataSet(train_images, train_labels, train_ids, train_cls)
152 | data_sets.valid = DataSet(validation_images, validation_labels, validation_ids, validation_cls)
153 |
154 | return data_sets
155 |
156 |
157 | def read_test_set(test_path, image_size):
158 | images, ids = load_test(test_path, image_size)
159 | return images, ids
160 |
--------------------------------------------------------------------------------
/dog.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rdcolema/tensorflow-image-classification/d9ca52018208a9cfbb7c74b95fab9d36d3f36d7a/dog.jpg
--------------------------------------------------------------------------------
/tf1_requirements.txt:
--------------------------------------------------------------------------------
1 | # Created with anaconda environment, non-conda requirements may differ
2 | # tensorflow-gpu compiled with cuda 9.0 and cudnn 7.6
3 |
4 | matplotlib==3.3.1
5 | nb_conda==2.2.1
6 | numpy==1.22.0
7 | pandas==0.25.0
8 | scikit-learn==1.5.0
9 | tensorflow==2.12.1
10 |
11 |
--------------------------------------------------------------------------------
/tf2_requirements.txt:
--------------------------------------------------------------------------------
1 | # Created with anaconda environment, non-conda requirements may differ
2 | # tensorflow-gpu compiled with cuda 10.1 and cudnn 7.6
3 |
4 | matplotlib==3.3.1
5 | nb_conda==2.2.1
6 | numpy==1.22.0
7 | pandas==1.1.0
8 | scikit-learn==1.5.0
9 | tensorflow==2.12.1
10 |
--------------------------------------------------------------------------------