The ability of a machine learning model to classify or label an image into its respective class with the help of learned features from hundreds of images is called as Image Classification.
Note: This tutorial is specific to Windows environment. Please modify code accordingly to work in other environments such as Linux and Max OS.
This is typically a supervised learning problem where we humans must provide training data (set of images along with its labels) to the machine learning model so that it learns how to discriminate each image (by learning the pattern behind each image) with respect to its label.
Update (03/07/2019): As Python2 faces end of life, the below code only supports Python3.
In this post, we will look into one such image classification problem namely Flower Species Recognition, which is a hard problem because there are millions of flower species around the world. As we know machine learning is all about learning from past data, we need huge dataset of flower images to perform real-time flower species recognition. Without worrying too much on real-time flower recognition, we will learn how to perform a simple image classification task using computer vision and machine learning algorithms with the help of Python.
A short clip of what we will be making at the end of the tutorial 😊
Update: After reading this post, you could look into my post on how to use state-of-the-art pretrained deep learning models such as Inception-V3, Xception, VGG16, VGG19, ResNet50, InceptionResNetv2 and MobileNet to this flower species recognition problem.
Project Idea
What if
- You build an intelligent system that was trained with massive dataset of flower/plant images.
- Your system predicts the label/class of the flower/plant using Computer Vision techniques and Machine Learning algorithms.
- Your system searches the web for all the flower/plant related data after predicting the label/class of the captured image.
- Your system helps gardeners and farmers to increase their productivity and yield with the help of automating tasks in garden/farm.
- Your system applies the recent technological advancements such as Internet of Things (IoT) and Machine Learning in the agricultural domain.
- You build such a system for your home or your garden to monitor your plants using a Raspberry Pi.
All the above scenarios need a common task to be done at the first place - Image Classification.
Yeah! It is classifying a flower/plant into it’s corresponding class or category. For example, when our awesome intelligent assistant looks into a Sunflower image, it must label or classify it as a “Sunflower”.
Classification Problem
Plant or Flower Species Classification is one of the most challenging and difficult problems in Computer Vision due to a variety of reasons.
Availability of plant/flower dataset
Collecting plant/flower dataset is a time-consuming task. You can visit the links provided at the bottom of this post where I have collected all the publicly available plant/flower datasets around the world. Although traning a machine with these dataset might help in some scenerios, there are still more problems to be solved.
Millions of plant/flower species around the world
This is something very interesting to look from a machine learning point of view. When I looked at the numbers in this link, I was frightened. Because, to accomodate every such species, we need to train our model with such large number of images with its labels. We are talking about 6 digit class labels here for which we need tremendous computing power (GPU farms).
High inter-class as well as intra-class variation
What we mean here is that “Sunflower” might be looking similar to a “Daffodil” in terms of color. This becomes an inter-class variation problem. Similarly, sometimes a single “Sunflower” image might have differences within it’s class itself, which boils down to intra-class variation problem.
Fine-grained classification problem
It means our model must not look into the image or video sequence and find “Oh yes! there is a flower in this image”. It means our model must tell “Yeah! I found a flower in this image and I can tell you it’s a tulip”.
Segmentation, View-point, Occlusion, Illumination and the list goes on..
Segmenting the plant/flower region from an image is a challenging task. This is because we might need to remove the unwanted background and take only the foreground object (plant/flower) which is again a difficult thing due to the shape of plant/flower.
Feature Extraction
Features are the information or list of numbers that are extracted from an image. These are real-valued numbers (integers, float or binary). There are a wider range of feature extraction algorithms in Computer Vision.
When deciding about the features that could quantify plants and flowers, we could possibly think of Color, Texture and Shape as the primary ones. This is an obvious choice to globally quantify and represent the plant or flower image.
But this approach is less likely to produce good results, if we choose only one feature vector, as these species have many attributes in common like sunflower will be similar to daffodil in terms of color and so on. So, we need to quantify the image by combining different feature descriptors so that it describes the image more effectively.
Global Feature Descriptors
These are the feature descriptors that quantifies an image globally. These don’t have the concept of interest points and thus, takes in the entire image for processing. Some of the commonly used global feature descriptors are
- Color - Color Channel Statistics (Mean, Standard Deviation) and Color Histogram
- Shape - Hu Moments, Zernike Moments
- Texture - Haralick Texture, Local Binary Patterns (LBP)
- Others - Histogram of Oriented Gradients (HOG), Threshold Adjancency Statistics (TAS)
Local Feature Descriptors
These are the feature descriptors that quantifies local regions of an image. Interest points are determined in the entire image and image patches/regions surrounding those interest points are considered for analysis. Some of the commonly used local feature descriptors are
- SIFT (Scale Invariant Feature Transform)
- SURF (Speeded Up Robust Features)
- ORB (Oriented Fast and Rotated BRIEF)
- BRIEF (Binary Robust Independed Elementary Features)
Combining Global Features
There are two popular ways to combine these feature vectors.
- For global feature vectors, we just concatenate each feature vector to form a single global feature vector. This is the approach we will be using in this tutorial.
- For local feature vectors as well as combination of global and local feature vectors, we need something called as Bag of Visual Words (BOVW). This approach is not discussed in this tutorial, but there are lots of resources to learn this technique. Normally, it uses Vocabulory builder, K-Means clustering, Linear SVM, and Td-Idf vectorization.
FLOWERS-17 dataset
We will use the FLOWER17 dataset provided by the University of Oxford, Visual Geometry group. This dataset is a highly challenging dataset with 17 classes of flower species, each having 80 images. So, totally we have 1360 images to train our model. For more information about the dataset and to download it, kindly visit this link.
Organizing Dataset
The folder structure for this example is given below.
folder structure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|--image-classification (folder)
|--|--dataset (folder)
|--|--|--train (folder)
|--|--|--|--cowbell (folder)
|--|--|--|--|--image_1.jpg
|--|--|--|--|--image_2.jpg
|--|--|--|--|--...
|--|--|--|--tulip (folder)
|--|--|--|--|--image_1.jpg
|--|--|--|--|--image_2.jpg
|--|--|--|--|--...
|--|--|--test (folder)
|--|--|--|--image_1.jpg
|--|--|--|--image_2.jpg
|--|--output (folder)
|--|--|--data.h5
|--|--|--labels.h5
|--|--global.py
|--|--train_test.py
Update (03/07/2019): To create the above folder structure and organize the training dataset folder, I have created a script for you - organize_flowers17.py. Please use this script first before calling any other script in this tutorial.
organize_flowers17.pycode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#-----------------------------------------
# DOWNLOAD AND ORGANIZE FLOWERS17 DATASET
#-----------------------------------------
import os
import glob
import datetime
import tarfile
import urllib.request
def download_dataset(filename, url, work_dir):
if not os.path.exists(filename):
print("[INFO] Downloading flowers17 dataset....")
filename, _ = urllib.request.urlretrieve(url + filename, filename)
statinfo = os.stat(filename)
print("[INFO] Succesfully downloaded " + filename + " " + str(statinfo.st_size) + " bytes.")
untar(filename, work_dir)
def jpg_files(members):
for tarinfo in members:
if os.path.splitext(tarinfo.name)[1] == ".jpg":
yield tarinfo
def untar(fname, path):
tar = tarfile.open(fname)
tar.extractall(path=path, members=jpg_files(tar))
tar.close()
print("[INFO] Dataset extracted successfully.")
#-------------------------
# MAIN FUNCTION
#-------------------------
if __name__ == '__main__':
flowers17_url = "http://www.robots.ox.ac.uk/~vgg/data/flowers/17/"
flowers17_name = "17flowers.tgz"
train_dir = "dataset"
if not os.path.exists(train_dir):
os.makedirs(train_dir)
download_dataset(flowers17_name, flowers17_url, train_dir)
if os.path.exists(train_dir + "\\jpg"):
os.rename(train_dir + "\\jpg", train_dir + "\\train")
# get the class label limit
class_limit = 17
# take all the images from the dataset
image_paths = glob.glob(train_dir + "\\train\\*.jpg")
# variables to keep track
label = 0
i = 0
j = 80
# flower17 class names
class_names = ["daffodil", "snowdrop", "lilyvalley", "bluebell", "crocus",
"iris", "tigerlily", "tulip", "fritillary", "sunflower",
"daisy", "coltsfoot", "dandelion", "cowslip", "buttercup",
"windflower", "pansy"]
# loop over the class labels
for x in range(1, class_limit+1):
# create a folder for that class
os.makedirs(train_dir + "\\train\\" + class_names[label])
# get the current path
cur_path = train_dir + "\\train\\" + class_names[label] + "\\"
# loop over the images in the dataset
for index, image_path in enumerate(image_paths[i:j], start=1):
original_path = image_path
image_path = image_path.split("\\")
image_file_name = str(index) + ".jpg"
os.rename(original_path, cur_path + image_file_name)
i += 80
j += 80
label += 1
Global Feature Extraction
Ok! It’s time to code!
We will use a simpler approach to produce a baseline accuracy for our problem. It means everything should work somehow without any error.
Our three global feature descriptors are
- Color Histogram that quantifies color of the flower.
- Hu Moments that quantifies shape of the flower.
- Haralick Texture that quantifies texture of the flower.
As you might know images are matrices, we need an efficient way to store our feature vectors locally. Our script takes one image at a time, extract three global features, concatenates the three global features into a single global feature and saves it along with its label in a HDF5 file format.
Insted of using HDF5 file-format, we could use “.csv” file-format to store the features. But, as we will be working with large amounts of data in future, becoming familiar with HDF5 format is worth it.
global.pycode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#-----------------------------------
# GLOBAL FEATURE EXTRACTION
#-----------------------------------
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import MinMaxScaler
import numpy as np
import mahotas
import cv2
import os
import h5py
#--------------------
# tunable-parameters
#--------------------
images_per_class = 80
fixed_size = tuple((500, 500))
train_path = "dataset/train"
h5_data = 'output/data.h5'
h5_labels = 'output/labels.h5'
bins = 8
- Lines 4 - 10 imports the necessary libraries we need to work with.
- Line 16 used to convert the input image to a fixed size of (500, 500).
- Line 17 is the path to our training dataset.
- Lines 18 - 19 stores our global features and labels in output directory.
- Line 20 is the number of bins for color histograms.
Functions for global feature descriptors
1. Hu Moments
To extract Hu Moments features from the image, we use cv2.HuMoments() function provided by OpenCV. The argument to this function is the moments of the image cv2.moments() flatenned. It means we compute the moments of the image and convert it to a vector using flatten(). Before doing that, we convert our color image into a grayscale image as moments expect images to be grayscale.
global.pycode
1
2
3
4
5
# feature-descriptor-1: Hu Moments
def fd_hu_moments(image):
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
feature = cv2.HuMoments(cv2.moments(image)).flatten()
return feature
2. Haralick Textures
To extract Haralick Texture features from the image, we make use of mahotas library. The function we will be using is mahotas.features.haralick(). Before doing that, we convert our color image into a grayscale image as haralick feature descriptor expect images to be grayscale.
global.pycode
1
2
3
4
5
6
7
8
# feature-descriptor-2: Haralick Texture
def fd_haralick(image):
# convert the image to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# compute the haralick texture feature vector
haralick = mahotas.features.haralick(gray).mean(axis=0)
# return the result
return haralick
3. Color Histogram
To extract Color Histogram features from the image, we use cv2.calcHist() function provided by OpenCV. The arguments it expects are the image, channels, mask, histSize (bins) and ranges for each channel [typically 0-256). We then normalize the histogram using normalize() function of OpenCV and return a flattened version of this normalized matrix using flatten().
global.pycode
1
2
3
4
5
6
7
8
9
10
# feature-descriptor-3: Color Histogram
def fd_histogram(image, mask=None):
# convert the image to HSV color-space
image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# compute the color histogram
hist = cv2.calcHist([image], [0, 1, 2], None, [bins, bins, bins], [0, 256, 0, 256, 0, 256])
# normalize the histogram
cv2.normalize(hist, hist)
# return the histogram
return hist.flatten()
Important: To get the list of training labels associated with each image, under our training path, we are supposed to have folders that are named with the labels of the respective flower species name inside which all the images belonging to that label are kept. Please keep a note of this as you might get errors if you don't have a proper folder structure.
global.pycode
1
2
3
4
5
6
7
8
9
10
# get the training labels
train_labels = os.listdir(train_path)
# sort the training labels
train_labels.sort()
print(train_labels)
# empty lists to hold feature vectors and labels
global_features = []
labels = []
1
['bluebell', 'buttercup', 'coltsfoot', 'cowslip', 'crocus', 'daffodil', 'daisy', 'dandelion', 'fritillary', 'iris', 'lilyvalley', 'pansy', 'snowdrop', 'sunflower', 'tigerlily', 'tulip', 'windflower']
For each of the training label name, we iterate through the corresponding folder to get all the images inside it. For each image that we iterate, we first resize the image into a fixed size. Then, we extract the three global features and concatenate these three features using NumPy’s np.hstack() function. We keep track of the feature with its label using those two lists we created above - labels and global_features. You could even use a dictionary here. Below is the code snippet to do these.
global.pycode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# loop over the training data sub-folders
for training_name in train_labels:
# join the training data path and each species training folder
dir = os.path.join(train_path, training_name)
# get the current training label
current_label = training_name
# loop over the images in each sub-folder
for x in range(1,images_per_class+1):
# get the image file name
file = dir + "/" + str(x) + ".jpg"
# read the image and resize it to a fixed-size
image = cv2.imread(file)
image = cv2.resize(image, fixed_size)
####################################
# Global Feature extraction
####################################
fv_hu_moments = fd_hu_moments(image)
fv_haralick = fd_haralick(image)
fv_histogram = fd_histogram(image)
###################################
# Concatenate global features
###################################
global_feature = np.hstack([fv_histogram, fv_haralick, fv_hu_moments])
# update the list of labels and feature vectors
labels.append(current_label)
global_features.append(global_feature)
print("[STATUS] processed folder: {}".format(current_label))
print("[STATUS] completed Global Feature Extraction...")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[STATUS] processed folder: bluebell
[STATUS] processed folder: buttercup
[STATUS] processed folder: coltsfoot
[STATUS] processed folder: cowslip
[STATUS] processed folder: crocus
[STATUS] processed folder: daffodil
[STATUS] processed folder: daisy
[STATUS] processed folder: dandelion
[STATUS] processed folder: fritillary
[STATUS] processed folder: iris
[STATUS] processed folder: lilyvalley
[STATUS] processed folder: pansy
[STATUS] processed folder: snowdrop
[STATUS] processed folder: sunflower
[STATUS] processed folder: tigerlily
[STATUS] processed folder: tulip
[STATUS] processed folder: windflower
[STATUS] completed Global Feature Extraction...
After extracting features and concatenating it, we need to save this data locally. Before saving this data, we use something called LabelEncoder() to encode our labels in a proper format. This is to make sure that the labels are represented as unique numbers. As we have used different global features, one feature might dominate the other with respect to it’s value. In such scenarios, it is better to normalize everything within a range (say 0-1). Thus, we normalize the features using scikit-learn’s MinMaxScaler() function. After doing these two steps, we use h5py to save our features and labels locally in .h5 file format. Below is the code snippet to do these.
global.pycode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# get the overall feature vector size
print("[STATUS] feature vector size {}".format(np.array(global_features).shape))
# get the overall training label size
print("[STATUS] training Labels {}".format(np.array(labels).shape))
# encode the target labels
targetNames = np.unique(labels)
le = LabelEncoder()
target = le.fit_transform(labels)
print("[STATUS] training labels encoded...")
# scale features in the range (0-1)
scaler = MinMaxScaler(feature_range=(0, 1))
rescaled_features = scaler.fit_transform(global_features)
print("[STATUS] feature vector normalized...")
print("[STATUS] target labels: {}".format(target))
print("[STATUS] target labels shape: {}".format(target.shape))
# save the feature vector using HDF5
h5f_data = h5py.File(h5_data, 'w')
h5f_data.create_dataset('dataset_1', data=np.array(rescaled_features))
h5f_label = h5py.File(h5_labels, 'w')
h5f_label.create_dataset('dataset_1', data=np.array(target))
h5f_data.close()
h5f_label.close()
print("[STATUS] end of training..")
1
2
3
4
5
6
7
[STATUS] feature vector size (1360, 532)
[STATUS] training Labels (1360,)
[STATUS] training labels encoded...
[STATUS] feature vector normalized...
[STATUS] target labels: [ 0 0 0 ..., 16 16 16]
[STATUS] target labels shape: (1360,)
[STATUS] end of training..
Notice that there are 532 columns in the global feature vector which tells us that when we concatenate color histogram, haralick texture and hu moments, we get a single row with 532 columns. So, for 1360 images, we get a feature vector of size (1360, 532). Also, you could see that the target labels are encoded as integer values in the range (0-16) denoting the 17 classes of flower species.
Training classifiers
After extracting, concatenating and saving global features and labels from our training dataset, it’s time to train our system. To do that, we need to create our Machine Learning models. For creating our machine learning model’s, we take the help of scikit-learn.
We will choose Logistic Regression, Linear Discriminant Analysis, K-Nearest Neighbors, Decision Trees, Random Forests, Gaussian Naive Bayes and Support Vector Machine as our machine learning models. To understand these algorithms, please go through Professor Andrew NG’s amazing Machine Learning course at Coursera or you could look into this awesome playlist of Dr.Noureddin Sadawi.
Furthermore, we will use train_test_split function provided by scikit-learn to split our training dataset into train_data and test_data. By this way, we train the models with the train_data and test the trained model with the unseen test_data. The split size is decided by the test_size parameter.
We will also use a technique called K-Fold Cross Validation, a model-validation technique which is the best way to predict ML model’s accuracy. In short, if we choose K = 10, then we split the entire data into 9 parts for training and 1 part for testing uniquely over each round upto 10 times. To understand more about this, go through this link.
We import all the necessary libraries to work with and create a models list. This list will have all our machine learning models that will get trained with our locally stored features. During import of our features from the locally saved .h5 file-format, it is always a good practice to check its shape. To do that, we make use of np.array() function to convert the .h5 data into a numpy array and then print its shape.
train_test.pycode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#-----------------------------------
# TRAINING OUR MODEL
#-----------------------------------
import h5py
import numpy as np
import os
import glob
import cv2
import warnings
from matplotlib import pyplot
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.model_selection import KFold, StratifiedKFold
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
from sklearn.externals import joblib
warnings.filterwarnings('ignore')
#--------------------
# tunable-parameters
#--------------------
num_trees = 100
test_size = 0.10
seed = 9
train_path = "dataset/train"
test_path = "dataset/test"
h5_data = 'output/data.h5'
h5_labels = 'output/labels.h5'
scoring = "accuracy"
# get the training labels
train_labels = os.listdir(train_path)
# sort the training labels
train_labels.sort()
if not os.path.exists(test_path):
os.makedirs(test_path)
# create all the machine learning models
models = []
models.append(('LR', LogisticRegression(random_state=seed)))
models.append(('LDA', LinearDiscriminantAnalysis()))
models.append(('KNN', KNeighborsClassifier()))
models.append(('CART', DecisionTreeClassifier(random_state=seed)))
models.append(('RF', RandomForestClassifier(n_estimators=num_trees, random_state=seed)))
models.append(('NB', GaussianNB()))
models.append(('SVM', SVC(random_state=seed)))
# variables to hold the results and names
results = []
names = []
# import the feature vector and trained labels
h5f_data = h5py.File(h5_data, 'r')
h5f_label = h5py.File(h5_labels, 'r')
global_features_string = h5f_data['dataset_1']
global_labels_string = h5f_label['dataset_1']
global_features = np.array(global_features_string)
global_labels = np.array(global_labels_string)
h5f_data.close()
h5f_label.close()
# verify the shape of the feature vector and labels
print("[STATUS] features shape: {}".format(global_features.shape))
print("[STATUS] labels shape: {}".format(global_labels.shape))
print("[STATUS] training started...")
1
2
3
[STATUS] features shape: (1360, 532)
[STATUS] labels shape: (1360,)
[STATUS] training started...
As I already mentioned, we will be splitting our training dataset into train_data as well as test_data. train_test_split() function does that for us and it returns four variables as shown below.
train_test.pycode
1
2
3
4
5
6
7
8
9
10
11
# split the training and testing data
(trainDataGlobal, testDataGlobal, trainLabelsGlobal, testLabelsGlobal) = train_test_split(np.array(global_features),
np.array(global_labels),
test_size=test_size,
random_state=seed)
print("[STATUS] splitted train and test data...")
print("Train data : {}".format(trainDataGlobal.shape))
print("Test data : {}".format(testDataGlobal.shape))
print("Train labels: {}".format(trainLabelsGlobal.shape))
print("Test labels : {}".format(testLabelsGlobal.shape))
1
2
3
4
5
[STATUS] splitted train and test data...
Train data : (1224, 532)
Test data : (136, 532)
Train labels: (1224,)
Test labels : (136,)
Notice we have decent amount of train_data and less test_data. We always want to train our model with more data so that our model generalizes well. So, we keep test_size variable to be in the range (0.10 - 0.30). Not more than that.
Finally, we train each of our machine learning model and check the cross-validation results. Here, we have used only our train_data.
train_test.pycode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 10-fold cross validation
for name, model in models:
kfold = KFold(n_splits=10, random_state=seed)
cv_results = cross_val_score(model, trainDataGlobal, trainLabelsGlobal, cv=kfold, scoring=scoring)
results.append(cv_results)
names.append(name)
msg = "%s: %f (%f)" % (name, cv_results.mean(), cv_results.std())
print(msg)
# boxplot algorithm comparison
fig = pyplot.figure()
fig.suptitle('Machine Learning algorithm comparison')
ax = fig.add_subplot(111)
pyplot.boxplot(results)
ax.set_xticklabels(names)
pyplot.show()
1
2
3
4
5
6
7
LR: 0.501719 (0.051735)
LDA: 0.441197 (0.034820)
KNN: 0.362742 (0.025958)
CART: 0.474690 (0.041314)
RF: 0.643809 (0.029491)
NB: 0.361102 (0.034966)
SVM: 0.043343 (0.027239)
As you can see, the accuracies are not so good. Random Forests (RF) gives the maximum accuracy of 64.38%. This is mainly due to the number of images we use per class. We need large amounts of data to get better accuracy. For example, for a single class, we atleast need around 500-1000 images which is indeed a time-consuming task. But, in this post, I have provided you with the steps, tools and concepts needed to solve an image classification problem.
Testing the best classifier
Let’s quickly try to build a Random Forest model, train it with the training data and test it on some unseen flower images.
train_test.pycode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#-----------------------------------
# TESTING OUR MODEL
#-----------------------------------
# to visualize results
import matplotlib.pyplot as plt
# create the model - Random Forests
clf = RandomForestClassifier(n_estimators=num_trees, random_state=seed)
# fit the training data to the model
clf.fit(trainDataGlobal, trainLabelsGlobal)
# loop through the test images
for file in glob.glob(test_path + "/*.jpg"):
# read the image
image = cv2.imread(file)
# resize the image
image = cv2.resize(image, fixed_size)
####################################
# Global Feature extraction
####################################
fv_hu_moments = fd_hu_moments(image)
fv_haralick = fd_haralick(image)
fv_histogram = fd_histogram(image)
###################################
# Concatenate global features
###################################
global_feature = np.hstack([fv_histogram, fv_haralick, fv_hu_moments])
# scale features in the range (0-1)
scaler = MinMaxScaler(feature_range=(0, 1))
rescaled_feature = scaler.fit_transform(global_feature)
# predict label of test image
prediction = clf.predict(rescaled_feature.reshape(1,-1))[0]
# show predicted label on image
cv2.putText(image, train_labels[prediction], (20,30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0,255,255), 3)
# display the output image
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.show()
As we can see, our approach seems to do pretty good at recognizing flowers. But it also predicted wrong label like the last one. Instead of sunflower, our model predicted buttercup.
You can download the entire code used in this post here.
Improving classifier accuracy
So, how are we going to improve the accuracy further? Fortunately, there are multiple techniques to achieve better accuracy. Some of them are listed below.
- Gather more data for each class. (500-1000) images per class.
- Use Data Augmentation to generate more images per class.
- Global features along with local features such as SIFT, SURF or DENSE could be used along with Bag of Visual Words (BOVW) technique.
- Local features alone could be tested with BOVW technique.
- Convolutional Neural Networks - State-of-the-art models when it comes to Image Classification and Object Recognition.
Some of the state-of-the-art Deep Learning CNN models are mentioned below.
- AlexNet
- Inception-V3
- Xception
- VGG16
- VGG19
- OverFeat
- ZeilerNet
- MSRA
But to apply CNN to this problem, the size of our dataset must be large enough and also to process those tremendous amount of data it is always recommended to use GPUs.
References
Research Papers
- A Visual Vocabulary for Flower Classification
- Delving into the whorl of flower segmentation
- Automated flower classification over a large number of classes
- Fine-Grained Plant Classification Using Convolutional Neural Networks for Feature Extraction
- Fine-tuning Deep Convolutional Networks for Plant Recognition
- Plant species classification using deep convolutional neural network
- Plant classification using convolutional neural networks
- Deep-plant: Plant identification with convolutional neural networks
- Deep Neural Networks Based Recognition of Plant Diseases by Leaf Image Classification
- Plant Leaf Identification via A Growing Convolution Neural Network with Progressive Sample Learning
Libraries and Tools
Datasets
In case if you found something useful to add to this article or you found a bug in the code or would like to improve some points mentioned, feel free to write it down in the comments. Hope you found something useful here.


pyxley.filters package¶
Submodules¶
pyxley.filters.filters module¶
-
class
pyxley.filters.filters.
ApiButton
(label, url, filter_id, default, route_func)¶ Bases:
pyxley.filters.filters.Filter
Selections populated by API.
A dropdown selection button based on the React-Bootstrap Dropdown. http://react-bootstrap.github.io/components.html#buttons
The selections are created by calling an endpoint.
Parameters: - label (str) – label of the button.
- url (str) – name of endpoint to create.
- filter_id (str) – alias of the filter.
- default (str) – default selection.
- route_func (function) – endpoint function to serve the options.
-
class
pyxley.filters.filters.
ConditionalButton
(labels, items, filter_ids, defaults, filter_style='btn-group')¶ Bases:
pyxley.filters.filters.Filter
Conditional Button Group.
A group of two buttons where the parent choice affects the child choice.
Parameters: - labels (list) – labels of the button.
- items (dict) – choices, children are nested.
- filter_ids (list) – list of aliases for the filter.
- defaults (list) – default selections.
- filter_style – css for the buttons.
-
class
pyxley.filters.filters.
DownloadButton
(label, url, route_func)¶ Bases:
pyxley.filters.filters.Filter
Download Button.
A simple download button.
Parameters: - label (str) – label of the button.
- url (str) – endpoint to call upon click.
- route_func (function) – endpoint function.
-
class
pyxley.filters.filters.
DynamicTextInput
(url, filter_id, default, route_func, placeholder='Enter text...', help='')¶ Bases:
pyxley.filters.filters.Filter
Dynamic Text Selection.
A combination search bar plus drop down inspired by: https://realpython.com/blog/python/the-ultimate-flask-front-end-part-2/
The selections are created by calling an endpoint.
Parameters: - url (str) – name of endpoint to create.
- filter_id (str) – alias of the filter.
- default (str) – default selection.
- route_func (function) – endpoint function to serve the options.
- placeholder (str) – default text.
- help (str) – help text.
-
class
pyxley.filters.filters.
Filter
(component_type, options, route_func)¶ Bases:
pyxley.ui.UIComponent
Base class for Filter UIComponent
-
name
= 'Filter'¶
-
-
class
pyxley.filters.filters.
SelectButton
(label, items, filter_id, default)¶ Bases:
pyxley.filters.filters.Filter
Dropdown Button.
A dropdown selection button based on the React-Bootstrap Dropdown. http://react-bootstrap.github.io/components.html#buttons
Parameters: - label (str) – label of the button.
- items (dict) – choices, children are nested.
- filter_id (str) – alias of the filter.
- default (str) – default selection.
-
class
pyxley.filters.filters.
SliderInput
(label, min_val, max_val, filter_id, default, step=1)¶ Bases:
pyxley.filters.filters.Filter
Simple Slider input.
A slider bar.
Parameters: - label (str) – label of the slider.
- min_val (int) – minimum value.
- max_val (int) – maximum value.
- filter_id (str) – list of aliases for the filter.
- default (int) – default selection.
- step (int) – size of the step associated with the slider.


How to Play
Pushfight is a game for two players. The goal of the game is to push one of your opponent's pieces off the board, or to prevent your opponent from having a legal push at the end of his or her turn.
Setup
The first player starts by placing its three square pieces and two round pieces anywhere on the top half of the board. The second player then responds, placing all five of its pieces on the bottom half of the board.
Movement and Pushes
Player 1 takes the first turn. A turn consists of up to two moves followed by a push. A player may move any of its pieces to any square on the board it can reach by empty spaces. Pieces cannot move through occupied squares or off the board.
After finishing movement, the active player must make a push. To push, the active player chooses one of its square pieces. It then pushes an adjacent piece (up, down, left, or right) one square in that direction. Unless obstructed by a wall or anchored piece (see below), all pieces in a line from the pushing piece are moved one square in the direction of the push. If any piece leaves the board, the owner of that piece loses and the other player wins. After completing a push, the player's turn ends and the opponent's turn begins. Play continues until one player wins.
A push must:
- Start with one of the active player's square pieces;
- Move at least one additional piece;
- Not be prevented by a wall or anchored piece.
A push may:
- Be in any direction, provided at least one other piece is in that direction;
- Push pieces of either color;
- Cause the pushing player to lose the game by pushing his or her own piece from the board.
Anchored Pieces
After a player pushes a piece, that piece becomes anchored until the start of owner's next turn. This is the only piece that cannot be pushed. There will never be more than one anchored piece on the board at any time.


Next: ->UNSPECIFIC as a Component Value, Previous: NIL as a Component Value, Up: Interpreting Pathname Component Values
If :wild is the value of a pathname component, that component is considered to be a wildcard, which matches anything.
A conforming program must be prepared to encounter a value of :wild as the value of any pathname component, or as an element of a list that is the value of the directory component.
When constructing a pathname, a conforming program may use :wild as the value of any or all of the directory, name, type, or version component, but must not use :wild as the value of the host, or device component.
If :wild is used as the value of the directory component in the construction of a pathname, the effect is equivalent to specifying the list (:absolute :wild-inferiors), or the same as (:absolute :wild) in a file system that does not support :wild-inferiors.


Ruby Classes
Classes in Ruby demystified.
June 1, 2015
Topics:
- What are classes?
- When, why, and how to use classes
- Classes can model a real-world object
Classes
In programming we work with objects and they have certain characteristics or properties. Like a car for example it has four (4) wheels, four (4) doors, one (1) engine and a quantity of passengers. Many other "things" or parts of the car can be listed but this is for the sake of the example. So when creating a Car in programming you have to set those characteristics, how many doors is the car going to have? how many passengers? how many wheels?
Now that we understand how OOP (Object Oriented Programming) works we can go straight to the definition. A class is a blueprint of the object it has the properties it needs to be set when created so it can do its "proper" functionality. No car ever is going to be created without wheels or passengers right? For now tho... hehe
When to use classes?
Classes are used when creating objects. If you have a program that needs interaction of certaing objects there must be a class per object. Each object that is created has a parent that is its class. The relation is 1Class to n-number of objects.
Why classes?
Classes let us organize the characteristics of the objects and its functionality. And as we use OOP this type of organization lets objects interact based on their creation, characteristics and functionality.
How to use classes?
COMING SOON...
Classes similar to real world?
Classes make us go one step closer to similutaing the realworld in OOP. You can see it as a real situation implemented in programming. Let's say we want to literally create humans in a program for a database of a school. So the class would be called "Person". Can you think of any characteristics that person would have? I do: age, height,weight, hair-color and guardians-name. So you see everytime you create a student (from the class Person) you will have to set those properties and assign them a value. This is how close to the real world it gets.
Hope this helped!
@devgmtech


EMERGENCY
HOME
MEDICAL TIPS
SETTINGS
FAQS
HOME
MEDICAL TIPS
SETTINGS
FAQS
REQUEST FIRST RESPONDER
MEDICAL TIPS


--- title: CSS Naked Day 2008 date: 2008-04-08 15:45:47 +1000 modified: 2008-04-09 01:45:47 +1000 uid: http://paul.annesley.cc/2008/08/css-naked-day layout: post ---
Tomorrow is CSS Naked Day '08, and websites around the intertubes will be stripping their styles and showing off their semantic markup.
I figured I'd get the ball rolling a little early, to balance the fact that I'll inevitably forget to re-enable the styles for at least a week afterwards.


Please enable javascript and refresh the page
Instructions are available on enable-javascript.com
This is a test page for hwcrypto.js (more information) implementing API v0.1
hwcrypto.sign() (This asks the user for a certificate via hwcrypto.getCertificate() and uses it to sign a hash specified above)
hwcrypto.sign()
hwcrypto.getCertificate()
hwcrypto.debug() | Clear log
hwcrypto.debug()


Work1
Gyaf website demo
A joomla website for NGO Gyaf
A joomla website for NGO Gyaf
Create a sample landing page using bootstrap
A collection of games I've created from the Odin Project
A portfolio website to showcase your work
This is a one page product example
A commercial page to use for products
I'm a junior web developer with a side interest for web design and user experience. I have vast experience in creating responsive and fast-performing websites. You can reach out to me at:


数字化物流规划平台
P&D
Create account to see it in action.
注册
Already have an account?
Login
© 2015-2021 环球物流咨询


ONE TRUE THING A film review by Matt Williams
RATING: * * 1/2* out of * * * *
Director Carl Franklin tackles this small-scale tearjerker, based on Anna Quindlen's novel One True Thing. Although good, and appropriately sad, it never reaches its full potential.
Ellen Gulden (Renee Zellweger) never had a good relationship with her mother, Kate (Meryl Streep). Instead, she has always idolized her father, George (William Hurt), a writer of moderate reknown. Following in his footsteps, she is well on her way toward a career as a feature writer at a New York magazine.
But, everything changes when she learns her mom has cancer. Someone needs to stay home and take care of Kate until she feels better, and Ellen has been drafted for the job. Reluctant at first, she soon learns how everything her mother does is much more difficult and underappreciated than she originally thought.
Those duties aren't made any easier by the approach of the holiday season. Kate is thoroughly involved in the community, and her women's group, the Minnies, administer various holiday activities and functions as well as decorating the entire town. And all this falls in Ellen's lap, who doesn't have a decorative bone in her entire body.
If all this sounds like the ingredients for a sentimental tearjerker, well, it is. However, although blatantly sentimental, the movie never gets as downright weepy as similar tales. This is both a good and a bad thing. It's good in that the film doesn't overplay its emotional hand...but bad in that a movie of this caliber should be able to tap those depths of emotion without overplaying its hand.
The problem with One True Thing can't be traced to one specific element of the film. Most everything here is done well and good. The problem is that there's little that is great in the film.
Case in point, take the performances. Streep is good as the ailing mom, but it's a performance which won't rank high along with her illustrious career. Zellweger holds her own in a rather large role in the company of rather prominent actors. William Hurt seems to drift through his part, still recovering from his Lost in Space misadventure. (As a side note, he actually gets to mutter "Danger Will Robinson" at one point in the film.)
Although a nice little sentimental film, One True Thing never taps its potential. It is alternately entertaining and sad, but not the three-hankie tearjerker it strives to be. One True Thing tops out at one hankie, if that.
Copyright 1998 Matt Williams
- Matt Williams<EMAIL_ADDRESS>Reviewer for Cinematter: http://www.cinematter.com Home of over 600 reviews, and information on nearly 700 upcoming releases
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


Before Change
def comicmain(request, page_title=""):
show content as main page item. Loads pages from the main project
challenge_short_name = settings.MAIN_PROJECT_NAME
if Challenge.objects.filter(short_name=challenge_short_name).count() == 0:
link = reverse("challenges:create")
link = link + "?short_name=%s" % challenge_short_name
link_html = create_HTML_a(
After Change
)
if page_title:
pages = [p for p in pages if p.title.lower() == page_title.lower()]
if len(pages) != 1:
raise ValueError(
f"More than 1 page with title {page_title} was found for {site}"
)
page = pages[0]
page.html = renderTags(request, page)
return render(request, "page.html", {"currentpage": page})


Best Practices by Data Life Cycle
All
Plan
Collect
Assure
Describe
Preserve
Discover
Integrate
Analyze
Learn more:
BP Primer


RB Leipzig vs 1. FC Koln Match Preview and Betting Tips – 2019/20 Bundesliga
by Football BettingRB Leipzig’s meteoric rise in German football is something many teams will take inspiration from.  This time, they are even competing for the silverware.
With 21 points from 11 games, Julian Nagelsmann’s team is sitting at the second place. They are four points adrift of first-placed Borussia Monchengladbach. A win on Saturday (November 23) might help them close the gap.
Their opponents for Saturday is 1. FC Koln, a side that gained promotion to the German top flight this season, but has found life harsh in the Bundesliga. With seven points, the Billy Goats are struggling at the penultimate position of the points table.
Last Meeting between the sides
When RB Leipzig last welcomed Koln at the Red Bull Arena in Leipzig in February of last year, they had a very tough time. Jean-Kevin Augustin gave the hosts an early lead, but two late goals from Vincent Koziello and Leonardo Bittencourt earned the visitors all three points.
1. FC Koln is not posing a threat
Though Koln managed to get better of the Red Bulls last time around, things have changed a lot ever since. With their brilliant performances, Leipzig are now sitting above both Bayern Munich and Borussia Dortmund.
Hence, Koln is not supposed to pose much of a threat. A victory for Nagelsmann’s side is trading at 1.3 on Bigpesa, whereas a win for Koln is trading at 6.8. A draw is trading at 4.9.
RB Leipzig’s attack unlikely to show mercy
One of the main reasons behind Leipzig’s current position in the points table is their attack Timo Werner and his compatriots have done a great jon so far, and have scored 29 goals this Bundesliga, joint-highest with Bayern Munich.
Hence, goals are expected at the Red Bull Arena on Saturday. The goal range of 2-3 is the most popular option, and is trading at 2.23. Right behind it is the goal range of 4-5, which is trading at 2.7. Over 2.5 goals is trading at 1.35, whereas under 2.5 goals is trading at 2.65.
Timo Werner likely to put pressure on leading goal-scorer Robert Lewandowski
Of the 19 goals Nagelsmann’s team has scored this season, 11 has been netted by Timo Werner alone. The German striker is currently the second-highest goal-scorer of the league. He is the likeliest goal-scorer for Saturday. A goal from Werner is trading at 1.37.
Checkout the odds at BigPesa.


{% load i18n %}
{% url 'badge-view' award.badge.id as the_url %}
{% blocktrans with name=award.badge.name icon=award.badge.icon desc=award.badge.desc %}Congratulations! You have won the
{{ name }}
badge for the notable accomplishment of: {{ desc }}.{% endblocktrans %}
{% if award.context %}
{% blocktrans with context=award.context|safe %}You have earned this honor for: {{ context }}{% endblocktrans %}
{% endif %}


Kanchipuram is part of Tondaimandalam
Kanchipuram is 72 km away from kanpur Chennai
It is the administrative headquarters of Kancheepuram District.
Kanchipuram is well-connected by road and rail
Chennai International Airport is the nearest domestic and
international airport to the town, which is located at Tirusulam in
Kanchipuram District
Formula of Water=H2O
Formula of Alcohal=C2H5OH
x2+y2=10


Trails
Clear
{{boundary.properties.MGRINST}}
Click on a trail to view details
Boundary:
Trail type:
No trails yet, why don't you
create one
?


Add A New Location
Locations
Location Name:
Minimum number of customer
Maximum number of customer
Average of cookies
Back To Home


Clinet-side Javascript file. It loads the page adn any user interaction is
send through to the server
- Source:
Methods
(inner) changeDIV - If user wants to go from login page to sign up page()
- Source:
(inner) checkPasswordList - After login button has been clicked this displays the login div(userNameInput, passwordInput, json)
Parameters:
| Name | Type | Description |
|---|---|---|
userNameInput |
string | username inputted by user |
passwordInput |
string | password inputted by user |
json |
object | returned object from the server detailing all usernames with their respective passwords |
- Source:
(inner) checkUserList - After login button has been clicked this displays the login div(userNameInput, json)
Parameters:
| Name | Type | Description |
|---|---|---|
userNameInput |
string | username inputted by user |
json |
object | returned object from the server detailing all usernames with their respective passwords |
- Source:
(inner) fnLogin - Takes user's inputted details and checks the databse to see if this user has an account and if the password matches the one stored.()
- Source:
(inner) getUserList - After login button has been clicked this displays the login div(userName, password, cb)
Parameters:
| Name | Type | Description |
|---|---|---|
userName |
string | username inputted by user |
password |
string | password inputted by user |
cb |
string | callback for statusOnNameAvailability |
- Source:
(inner) showLogin - After login button has been clicked this displays the login div()
- Source:
(inner) showRegister - After signup button has been clicked this displays the sign up div()
- Source:


Preparation time
less than 30 mins
Cooking time
10 to 30 mins
Serves
Serves 1
Dietary
This delicious puff pastry vegetable tart is great for kids to cook. You can easily multiply up the ingredients to make more.
Before cooking, wash your hands and put on an apron. Weigh or measure all the ingredients. Preheat the oven to 220C/425F/Gas 7. Line a baking tray with baking paper and brush it generously with vegetable oil.
Add the tomatoes to a mixing bowl then cut them into pieces with the scissors.
Tear up the basil leaves and add them to the bowl. Add a pinch of paprika.
Cut off the roots and the dark green top of the spring onion with the scissors and throw them away. Then chop up the spring onion into small pieces and add it to the tomatoes along with the olive oil.
Pop open the pepper by putting your thumbs on the stalk and pressing inwards, and tear about a quarter of it into bite-sized pieces and add this to the other ingredients. Next add the tinned sweetcorn.
Roll out the pastry onto a lightly floured surface.
Trim the pastry into an even rectangle with the knife. Then use the tip of the knife to draw a border around the rectangle - like a picture frame - not cutting through the pastry completely.
Put the pastry onto the paper-lined baking tray - you need to do this now as it’s much harder to move once the topping is on.
Pile your vegetable filling into the central part of your pastry base and spread to the edges of the border. Bake for 15-20 minutes until the pastry is golden and risen. Serve your tart with carrot batons.


Home
CRE Form One
Introduction to CRE
What is Christian Religious Education
Reasons for Studying CRE
Past KCSE questions on the Topic
Introdution to Christian Religious Education
Reasons for Studying CRE
tab
Past KCSE questions on the Topic


Rock, Paper, Scissors!
Rock
Paper
Scissors
. . . [Section for API. Is there an API for different pictures of rocks, paper and scissors?]


Groupboard.co was built in collaboration with two other Wyncode Academy students as our final project. It is designed to be a communication tool between adults and the youth that are members of their organizations. We see it being used to facilitate the exchange of information between coaches and their student athletes, teachers and the members of their afterschool programs as well as many other groups. Our main focus in designing this site was to create a safe environment for updates and announcements that did not require either party to exchange personal information. This project was built in two weeks and we look forward to refining the site and adding additional features in the future.
@rtMarket was created in two days and presented to a group of friends and family members halfway through the course at Wyncode Academy. I worked on this project with two other students and we are proud of what we were able to accomplish. We had just begin learning how to utilize the Rails framework and amazed ourselves at how quickly we were able to utilize the skills we had just learned to build this with the impending deadline approaching.
I am a former teacher turned developer and completed the Wyncode Academy boot camp in August 2015 to fast track my learning. I learned an incredible amount during the course and worked with my peers to create some amazing projects during that time. I am looking forward to this next chapter of my life as a student of web development as I have found that this is a career in which one is always learning. I am seeking a full-time position and I hope to speak with you soon.
Languages/Frameworks: Ruby, Rails, HTML, CSS, Javascript
Concepts: Git, Agile Management, Pair Programming, OOP