Explanation
Package Components
TeachableMachine
Bases: object
Create a TeachableMachine object to run pre-trained AI models.
Source code in src/teachable_machine.py
class TeachableMachine(object):
"""
Create a TeachableMachine object to run pre-trained AI models.
"""
SUPPORTED_TYPES = {"keras", "h5"}
IMAGE_SIZE = (224, 224)
def __init__(
self,
model_path="keras_model.h5",
labels_file_path="labels.txt",
model_type="h5",
) -> None:
self._model_type = model_type.lower()
if self._model_type not in self.SUPPORTED_TYPES:
raise ValueError(
f"Unsupported model type: {self._model_type}. Use 'keras' or 'h5'."
)
np.set_printoptions(suppress=True)
self._load_model(model_path)
self._load_labels(labels_file_path)
print("Teachable Machine Object is created successfully.")
def _load_model(self, model_path: str):
try:
self._model = load_model(model_path, compile=False)
except IOError as e:
print("LoadingModelError: Error while loading Teachable Machine model")
raise IOError("Error loading model") from e
except Exception as e:
print("LoadingModelError: Error while loading Teachable Machine model")
raise FileNotFoundError("Model file not found") from e
def _load_labels(self, labels_file_path):
try:
with open(labels_file_path, "r") as file:
self._labels = file.readlines()
except IOError as e:
print("LoadingLabelsError: Error while loading labels.txt file")
raise IOError("Error loading labels") from e
except Exception as e:
print("LoadingLabelsError: Error while loading labels.txt file")
raise FileNotFoundError("Labels file not found") from e
def _open_image(self, image_path):
"""
Open an image file and convert it to RGB mode.
Parameters:
image_path (str): Path to the image file.
Returns:
PIL.Image.Image: Opened image in RGB mode.
"""
try:
return Image.open(image_path).convert("RGB")
except FileNotFoundError as e:
print("ImageNotFound: Error in image file.")
raise FileNotFoundError("Image file not found") from e
except Exception as e:
print("ImageTypeError: Error while opening or converting image")
raise TypeError("Unsupported image type") from e
def classify_image(self, image_path: str):
"""
Classify an image using the pre-trained model.
Parameters:
image_path (str): Path of the image to be classified.
Returns:
dict: Classification results including class name, index, confidence and predictions.
"""
image = self._open_image(image_path)
return self._get_image_classification(image)
def _get_image_classification(self, image):
data = self._preprocess_image(image)
prediction = self._model.predict(data)
class_index = np.argmax(prediction)
class_name = self._labels[class_index].strip()
class_confidence = prediction[0][class_index]
return {
"class_name": class_name,
"highest_class_name": class_name,
"highest_class_id": class_index,
"class_index": class_index,
"class_id": class_index,
"predictions": prediction[0],
"all_predictions": prediction[0],
"class_confidence": class_confidence,
"highest_class_confidence": class_confidence,
}
def _preprocess_image(self, image):
image = ImageOps.fit(image, self.IMAGE_SIZE, Image.Resampling.LANCZOS)
image_array = np.asarray(image)
normalized_image_array = (image_array.astype(np.float32) / 127.5) - 1
return np.expand_dims(normalized_image_array, axis=0)
def classify_and_show(self, image_path: str, convert_to_bgr=True):
"""
Classify an image and show the prediction results on the image.
Parameters:
image_path (str): Path of the input image to be classified.
convert_to_bgr (bool, optional): Whether to convert the image to BGR format for OpenCV.
If False, the image will be returned in RGB format. Default is True.
Returns:
tuple: (classification_result, image_with_prediction)
classification_result (dict): Classification results including class name, index, confidence and predictions.
image_with_prediction (np.ndarray or PIL.Image.Image): The image with prediction results drawn on it.
Returns a NumPy array in BGR format if convert_to_bgr is True.
Otherwise, returns a PIL.Image.Image in RGB format.
"""
classification_result = self.classify_image(image_path)
image_with_prediction = self.show_prediction_on_image(
image_path, classification_result, convert_to_bgr=convert_to_bgr
)
return classification_result, image_with_prediction
def show_prediction_on_image(
self, image_path: str, classification_result=None, convert_to_bgr=True
):
"""
Show the prediction results on the image and return the modified image.
Parameters:
image_path (str): Path of the input image to be classified.
classification_result (dict, optional): Pre-computed classification result.
If not provided, the method will classify the image.
convert_to_bgr (bool, optional): Whether to convert the image to BGR format for OpenCV.
If False, the image will be returned in RGB format. Default is True.
Returns:
np.ndarray or PIL.Image.Image: The image with prediction results drawn on it.
Returns a NumPy array in BGR format if convert_to_bgr is True.
Otherwise, returns a PIL.Image.Image in RGB format.
"""
image = self._open_image(image_path)
if classification_result is None:
classification_result = self._get_image_classification(image)
class_name = classification_result["class_name"]
confidence = classification_result["class_confidence"]
confidence_percent = confidence * 100
text = f"{class_name}: {confidence_percent:.2f}%"
draw = ImageDraw.Draw(image)
font_size = int(image.height * 0.04) # 4% of the image height
font = ImageFont.truetype("DejaVuSans-Bold.ttf", font_size)
text_width, text_height = draw.textsize(text, font=font)
position = (10, image.height - text_height - 10)
draw.rectangle(
[
position[0],
position[1],
position[0] + text_width,
position[1] + text_height,
],
fill=(0, 0, 0, 128),
)
draw.text(position, text, font=font, fill=(255, 255, 255))
if convert_to_bgr:
image_2_numpy_arr = np.array(image)
# Convert RGB to BGR
image_2_numpy_arr = image_2_numpy_arr[:, :, ::-1]
return image_2_numpy_arr
return image
classify_and_show(image_path, convert_to_bgr=True)
Classify an image and show the prediction results on the image.
Parameters: image_path (str): Path of the input image to be classified. convert_to_bgr (bool, optional): Whether to convert the image to BGR format for OpenCV. If False, the image will be returned in RGB format. Default is True.
tuple: (classification_result, image_with_prediction) classification_result (dict): Classification results including class name, index, confidence and predictions. image_with_prediction (np.ndarray or PIL.Image.Image): The image with prediction results drawn on it. Returns a NumPy array in BGR format if convert_to_bgr is True. Otherwise, returns a PIL.Image.Image in RGB format.
Source code in src/teachable_machine.py
def classify_and_show(self, image_path: str, convert_to_bgr=True):
"""
Classify an image and show the prediction results on the image.
Parameters:
image_path (str): Path of the input image to be classified.
convert_to_bgr (bool, optional): Whether to convert the image to BGR format for OpenCV.
If False, the image will be returned in RGB format. Default is True.
Returns:
tuple: (classification_result, image_with_prediction)
classification_result (dict): Classification results including class name, index, confidence and predictions.
image_with_prediction (np.ndarray or PIL.Image.Image): The image with prediction results drawn on it.
Returns a NumPy array in BGR format if convert_to_bgr is True.
Otherwise, returns a PIL.Image.Image in RGB format.
"""
classification_result = self.classify_image(image_path)
image_with_prediction = self.show_prediction_on_image(
image_path, classification_result, convert_to_bgr=convert_to_bgr
)
return classification_result, image_with_prediction
classify_image(image_path)
Classify an image using the pre-trained model.
Parameters: image_path (str): Path of the image to be classified.
Returns: dict: Classification results including class name, index, confidence and predictions.
Source code in src/teachable_machine.py
def classify_image(self, image_path: str):
"""
Classify an image using the pre-trained model.
Parameters:
image_path (str): Path of the image to be classified.
Returns:
dict: Classification results including class name, index, confidence and predictions.
"""
image = self._open_image(image_path)
return self._get_image_classification(image)
show_prediction_on_image(image_path, classification_result=None, convert_to_bgr=True)
Show the prediction results on the image and return the modified image.
Parameters: image_path (str): Path of the input image to be classified. classification_result (dict, optional): Pre-computed classification result. If not provided, the method will classify the image. convert_to_bgr (bool, optional): Whether to convert the image to BGR format for OpenCV. If False, the image will be returned in RGB format. Default is True.
np.ndarray or PIL.Image.Image: The image with prediction results drawn on it. Returns a NumPy array in BGR format if convert_to_bgr is True. Otherwise, returns a PIL.Image.Image in RGB format.
Source code in src/teachable_machine.py
def show_prediction_on_image(
self, image_path: str, classification_result=None, convert_to_bgr=True
):
"""
Show the prediction results on the image and return the modified image.
Parameters:
image_path (str): Path of the input image to be classified.
classification_result (dict, optional): Pre-computed classification result.
If not provided, the method will classify the image.
convert_to_bgr (bool, optional): Whether to convert the image to BGR format for OpenCV.
If False, the image will be returned in RGB format. Default is True.
Returns:
np.ndarray or PIL.Image.Image: The image with prediction results drawn on it.
Returns a NumPy array in BGR format if convert_to_bgr is True.
Otherwise, returns a PIL.Image.Image in RGB format.
"""
image = self._open_image(image_path)
if classification_result is None:
classification_result = self._get_image_classification(image)
class_name = classification_result["class_name"]
confidence = classification_result["class_confidence"]
confidence_percent = confidence * 100
text = f"{class_name}: {confidence_percent:.2f}%"
draw = ImageDraw.Draw(image)
font_size = int(image.height * 0.04) # 4% of the image height
font = ImageFont.truetype("DejaVuSans-Bold.ttf", font_size)
text_width, text_height = draw.textsize(text, font=font)
position = (10, image.height - text_height - 10)
draw.rectangle(
[
position[0],
position[1],
position[0] + text_width,
position[1] + text_height,
],
fill=(0, 0, 0, 128),
)
draw.text(position, text, font=font, fill=(255, 255, 255))
if convert_to_bgr:
image_2_numpy_arr = np.array(image)
# Convert RGB to BGR
image_2_numpy_arr = image_2_numpy_arr[:, :, ::-1]
return image_2_numpy_arr
return image