aigarmic ======== .. py:module:: aigarmic Submodules ---------- .. toctree:: :maxdepth: 1 /autoapi/aigarmic/file_handlers/index /autoapi/aigarmic/model/index /autoapi/aigarmic/plate/index /autoapi/aigarmic/process_plate_image/index /autoapi/aigarmic/train/index Classes ------- .. autoapisummary:: aigarmic.Plate aigarmic.PlateSet aigarmic.Model aigarmic.BinaryModel aigarmic.BinaryNestedModel aigarmic.SoftmaxModel Functions --------- .. autoapisummary:: aigarmic.plate_set_from_dir aigarmic.create_dataset_from_directory aigarmic.predict_colony_images_from_directory aigarmic.save_training_log aigarmic.get_paths_from_directory aigarmic.get_concentration_from_path aigarmic.convert_cv2_to_keras aigarmic.keras_image_to_cv2 aigarmic.find_threshold_value aigarmic.split_by_grid aigarmic.train_binary aigarmic.train_softmax aigarmic.visualise_training Package Contents ---------------- .. py:class:: Plate(drug: str, concentration: float, image: Optional[Union[str, cv2.typing.MatLike]] = None, n_row: Optional[int] = None, n_col: Optional[int] = None, growth_code_matrix: Optional[list[list[int]]] = None, visualise_contours: bool = False, model: Optional[aigarmic.model.Model] = None, key: Optional[list[str]] = None) .. py:method:: add_growth_code_matrix(growth_code_matrix: list[list[int]]) -> None .. py:method:: valid_growth_code_matrix(growth_code_matrix: list[list[int]]) -> bool .. py:method:: matrix_dimensions(matrix) -> tuple[int, Ellipsis] :staticmethod: Get dimensions of a matrix :param matrix: matrix to get dimensions of :raises ValueError: if matrix is not 2D or is not a valid matrix :return: tuple of dimensions .. py:method:: split_images(visualise_contours: bool = False) -> None Splits images into individual colony images using grid :param visualise_contours: Visualise the contours of the plate (useful for validation of grid splitting) .. py:method:: import_image(image: numpy.ndarray) -> None Import and save image of agar plate :param image: loaded using cv2.imread .. py:method:: get_colony_image(index: Optional[tuple[int, int]] = None) -> tuple[numpy.ndarray, str] Pulls colony image and associated code-stamp Code-stamps are strings containing, in sequence: - Antibiotic name - Antibiotic concentration - Row (i) index - Column (j) index If no index is provided (default) a random image is given @param index: tuple of row and column index @return: tuple of image and code-stamp (e.g., "drug_0.125_i_1_j_2") .. py:method:: link_model(model: aigarmic.model.Model) -> None Link model to plate for predictions :param model: Model to link .. py:method:: get_key() -> Optional[list[str]] Get key from linked Model :raises: LookupError: No linked model to get key from :return: Key (or None if one is not found) .. py:method:: set_key(key: list[str]) -> None Set plate key. Checks whether differs from linked model key (if any), and warns if different. :param key: List of growth categories (zero-indexed) .. py:method:: annotate_images(model: Optional[aigarmic.model.Model] = None) -> list[list[str]] Annotate plate images :param model: linked model to use for predictions :return: Two-dimensional list of growth annotations .. py:method:: print_matrix() -> None Print growth matrix in human-readable format .. py:method:: get_inaccurate_images(threshold: float = 0.9) -> set[tuple[int, int]] Get indexes of images with prediction accuracy below threshold :param threshold: Prediction threshold :return: Set containing indices of inaccurate images .. py:method:: review_poor_images(threshold: float = 0.9, save_dir: str = None) -> list[tuple[int, int]] Review and re-classify images with prediction accuracy below threshold. Classes should be zero indexed (e.g., 0, 1, 2). Currently, only supports up to 9. If save_dir provided then colony images will also be saved to a subdirectory (named after the new classification), to allow for future use in training. Enter new classification for each image using numbers (e.g., 0/1/2) on keyboard, press enter to skip, press esc to stop reviewing the plate. :param threshold: Prediction threshold to identify inaccurate images :param save_dir: Directory to save re-classified images :return: List of indices of re-classified images .. py:method:: convert_growth_codes(key: list[str]) -> list[list[str]] Convert growth codes to human-readable format using key E.g., [0, 1, 2] -> ["No growth", "Poor growth", "Good growth"] Sets self.growth_matrix :param key: List of growth codes (zero-indexed) :return: Growth matrix .. py:method:: __repr__() -> str Return repr(self). .. py:method:: __lt__(other) -> bool Return self bool Return self==value. .. py:method:: __gt__(other) -> bool Return self>value. .. py:method:: __le__(other) -> bool Return self<=value. .. py:method:: __ge__(other) -> bool Return self>=value. .. py:method:: __ne__(other) -> bool Return self!=value. .. py:method:: __hash__() -> int Return hash(self). .. py:class:: PlateSet(plates_list: list[Plate], key: Optional[list[str]] = None) .. py:method:: valid_dimensions() -> bool Check if all plates in PlateSet have the same x and y dimensions :return: True if all plates have the same dimensions, False otherwise .. py:method:: get_all_plates() -> list[Plate] Returns a sorted list of all plates in the PlateSet, including the control plate :return: List of Plate objects .. py:method:: convert_mic_matrix(mic_format: str = 'string') -> numpy.array Converts format of MIC matrix :param mic_format: Format to convert to (only "string" is supported) :return: matrix (array) of MIC values .. py:method:: calculate_mic(no_growth_key_items: tuple[int, Ellipsis]) -> numpy.array Calculate MIC matrix using image predictions. Sets self.mic_matrix :param no_growth_key_items: tuple of key items that should be classified as "no growth" for MIC purposes :return: MIC matrix .. py:method:: generate_qc() -> numpy.array Generate QC matrix for PlateSet, as follows: "F" = FAIL - no growth in positive control plate, result should be disregarded "W" = WARNING - more than one change in concentration gradient. There should only be one change at the MIC breakpoint (where the images change from growth to no/poor growth). Depending on the application of the results, manual confirmation should be considered for warnings. "P" = PASS - no QC issues found :return: Matrix of QC values (strings) .. py:method:: review_poor_images(threshold: float = 0.9, save_dir: Optional[str] = None) -> list[list[tuple[int, int]]] Review and re-classify images with prediction accuracy below threshold. Currently, supports up to 0--9 classes. If save_dir provided then colony images will also be saved to a subdirectory (named after the new classification), to allow for future use in training. Enter new classification for each image using 0/1/2 on keyboard, or press enter (or esc) to skip. :param threshold: Prediction threshold to identify inaccurate images :param save_dir: Directory to save re-classified images :return: List of indices of re-classified images .. py:method:: get_csv_data() -> list[dict] Get MIC and QC data in a format suitable for CSV export: List of dicts containing: - Antibiotic: Antibiotic name - Position: Position of the colony (e.g., A1, B2, etc.) - MIC: MIC value - QC: QC value (P, W, F) :return: List of dicts with MIC and QC data .. py:method:: __repr__() -> str Return repr(self). .. py:function:: plate_set_from_dir(path: Union[str, pathlib.Path], drug: str, model: aigarmic.model.Model, n_row: int = 8, n_col: int = 12, **kwargs) -> PlateSet Create a PlateSet from a directory of images. Images are annotated using the provided model. :param path: directory containing plate images (.jpg) with filenames indicating antibiotic concentration :param drug: name of drug :param model: model file to use for predictions :param n_row: number of rows in the plates :param n_col: number of columns in the plates :param kwargs: additional keyword arguments to pass to Plate constructor :return: PlateSet with MIC and QC values .. py:class:: Model(key: Optional[list[str]]) Bases: :py:obj:`abc.ABC` Helper class that provides a standard way to create an ABC using inheritance. .. py:method:: get_key() -> list[str] Return key to convert model output to human-readable label :return: .. py:method:: predict(image) -> dict :abstractmethod: .. py:class:: BinaryModel(path: str, trained_x: int, trained_y: int, key: Optional[list[str]], threshold: float = 0.5) Bases: :py:obj:`KerasModel` Helper class that provides a standard way to create an ABC using inheritance. .. py:method:: predict(image: numpy.ndarray) -> dict Predict growth category from image :param image: image loaded using cv2.imread :return: dictionary with keys 'prediction', 'score', 'growth_code', 'growth', 'accuracy' .. py:class:: BinaryNestedModel(first_line_model: BinaryModel, second_line_model: BinaryModel, first_model_accuracy_acceptance: float = 0.9, suppress_first_model_accuracy_check: bool = False) Bases: :py:obj:`Model` Helper class that provides a standard way to create an ABC using inheritance. .. py:method:: predict(image: numpy.ndarray) -> dict Predict colony growth from image :param image: image loaded using cv2.imread :return: dictionary with keys 'prediction', 'score', 'growth_code', 'growth', 'accuracy' .. py:class:: SoftmaxModel(path: str, trained_x: int, trained_y: int, key: Optional[list[str]]) Bases: :py:obj:`KerasModel` SoftmaxModel is a one-stop model when more than one growth category is present, e.g.: ['No growth', 'Poor growth', 'Good growth'] .. py:method:: predict(image: numpy.ndarray) -> dict Predict growth category from image :param image: loaded using cv2.imread :return: dictionary with keys 'prediction', 'score', 'growth_code', 'growth', 'accuracy' .. py:function:: create_dataset_from_directory(directory: str, label_mode: str, image_width: int, image_height: int, seed: int = 12345, val_split: float = 0.2, batch_size: int = 32) -> tuple[tensorflow.data.Dataset, tensorflow.data.Dataset] Create a training and validation dataset from a directory containing subdirectories for each class of image data. :param directory: path containing images, each in subdirectory corresponding to class :param label_mode: Labelling depending on model type ("binary" for binary or "int" for softmax) :param image_width: Image width in pixels :param image_height: Image height in pixels :param seed: Random seed for dataset splitting :param val_split: Proportion of data to use for validation :param batch_size: Batch size for datasets :return: Tuple containing training and validation datasets .. py:function:: predict_colony_images_from_directory(directory: Optional[Union[str, pathlib.Path]], model: tensorflow.keras.models.Model, class_names: list[str], image_width: int, image_height: int, model_type: str, save_path: Optional[Union[str, pathlib.Path]] = None, binary_threshold: float = 0.5) -> list[dict] Predict the class of images in a directory using a trained model, and compare prediction to true class (based on subdirectory in which image is located, which should correspond to class name). :param directory: Directory containing images to predict :param model: Model to use for prediction :param class_names: List of class names :param image_width: Image width in pixels :param image_height: Image height in pixels :param model_type: "binary" or "softmax" :param save_path: Path to save prediction log :param binary_threshold: For binary models, threshold for classifying as positive :return: List of dictionaries containing image, path, prediction, predicted class, and true class (for each image) .. py:function:: save_training_log(model_history: keras.callbacks.History, save_path: Union[str, pathlib.Path]) -> None Save training log to CSV file :param model_history: Training history object :param save_path: Directory to save training log .. py:function:: get_paths_from_directory(path: Union[str, pathlib.Path]) -> dict[str, list[str]] Returns a dict of abx_names: [image1_path, image2_path, etc.] If there are no antibiotic subdirectories, "unnamed" is used for abx_names (length = 1) :param path: Path to directory containing antibiotic subdirectories :return: dict of abx_names: [image1_path, image2_path, etc.] .. py:function:: get_concentration_from_path(path: Union[str, pathlib.Path]) -> float get concentration from plate image path, e.g. antibiotic1/0.125.jpg -> 0.125 :param path: Path to plate image :return: Concentration .. py:function:: convert_cv2_to_keras(image, size_x=160, size_y=160) -> numpy.ndarray Convert a cv2 image to a keras image :param image: Image loaded using cv2.imread :param size_x: Width to resize image to (pixels) :param size_y: Height to resize image to (pixels) :return: Image as a numpy array .. py:function:: keras_image_to_cv2(image: tensorflow.Tensor) -> numpy.ndarray Convert a keras image to a cv2 image :param image: Image as a tensor :return: Image as a numpy array .. py:function:: find_threshold_value(image: numpy.ndarray, look_for: int, start: int = 20, end: int = 100, by: int = 1, area_lower_bound: int = 1000) -> Optional[tuple[list, int]] Find threshold value that correctly splits an agar plate image into colony sub-images. Assumes that a black grid overlays the image. :param image: Image file loaded using cv2.imread :param look_for: target sub-images :param start: starting threshold value :param end: ending threshold value :param by: threshold increment value :param area_lower_bound: minimum area for a contour to be considered :return: tuple of contours and threshold value .. py:function:: split_by_grid(image: numpy.ndarray, n_rows: int, n_cols: int, visualise_contours: bool = False, plate_name: Optional[str] = None) -> list[list[numpy.ndarray]] Split an agar plate image into individual colony sub-images using a grid overlay. :param image: image file loaded using cv2.imread :param n_rows: number of rows in the grid :param n_cols: number of columns in the grid :param visualise_contours: if True, display the contours found (useful for validation) :param plate_name: name of plate to display in visualisation (useful for validation) :return: matrix of sub-images .. py:function:: train_binary(annotations_path, model_design: tensorflow.keras.models.Sequential, val_split: float = 0.2, image_width: int = 160, image_height: int = 160, batch_size: int = 64, epochs: int = 300, stop_training_threshold: float = 0.98, learning_rate: float = 0.0001) -> tuple[tensorflow.keras.models.Sequential, list[str], keras.callbacks.History, list] Train a binary classification model to differentiate between two classes of colony images. Provide a keras sequential model design to inform the neural network architecture. The final binary/sigmoid layer should not be included in the sequential model design, as this is added by the function. :param annotations_path: Path to directory containing annotated images, with subdirectories for each class (usually '0' and '1') :param model_design: Keras model design (Sequential) to inform neural network architecture, excluding final layer :param val_split: Validation split proportion :param image_width: Image width (pixels) :param image_height: Image height (pixels) :param batch_size: Training batch size :param epochs: Max number of training epochs :param stop_training_threshold: Accuracy threshold at which to accept model and stop training :param learning_rate: Learning rate for the optimizer :return: Tuple containing trained model, class names, training history, and evaluation results .. py:function:: train_softmax(annotations_path, model_design: tensorflow.keras.models.Sequential, val_split: float = 0.2, image_height: int = 160, image_width: int = 160, batch_size: int = 64, epochs: int = 300, stop_training_threshold: float = 0.98) -> tuple[tensorflow.keras.models.Sequential, list[str], keras.callbacks.History, list] Train a softmax classification model to differentiate between multiple classes (2 or more) of colony images. The final softmax layer should not be included in the sequential model design, as this is added by the function. :param annotations_path: Path to directory containing annotated images, with subdirectories for each class (e.g., '0', '1', '2', ...) :param model_design: Keras model design (Sequential) to inform neural network architecture, excluding final layer :param val_split: Validation split proportion :param image_width: Image width (pixels) :param image_height: Image height (pixels) :param batch_size: Training batch size :param epochs: Max number of training epochs :param stop_training_threshold: Accuracy threshold at which to accept model and stop training :return: Tuple containing trained model, class names, training history, and evaluation results .. py:function:: visualise_training(history: keras.callbacks.History) -> None Visualise training and validation accuracy and loss over epochs. :param history: Training history object (usually returned by model.fit(), train_binary(), or train_softmax())