Identifies plant species via camera and generates care instructions Python

👤 Sharing: AI
```python
# Import necessary libraries
import cv2
import numpy as np
from PIL import Image
from io import BytesIO  # For handling image data from the web, if needed
import requests #For handling image data from the web, if needed
from tensorflow.keras.models import load_model  # TensorFlow for image classification
# TensorFlow or other image recognition library might need to be installed:  pip install tensorflow opencv-python pillow requests

# Mock plant data (replace with a real database or API connection)
plant_database = {
    "rose": {
        "scientific_name": "Rosa spp.",
        "care_instructions": "Roses need at least 6 hours of sunlight per day. Water deeply when the top inch of soil is dry. Fertilize regularly during the growing season. Prune in late winter or early spring.",
        "watering_frequency": "Water deeply when the top inch of soil is dry.",
        "sunlight": "At least 6 hours of sunlight per day.",
        "fertilizing": "Fertilize regularly during the growing season.",
    },
    "sunflower": {
        "scientific_name": "Helianthus annuus",
        "care_instructions": "Sunflowers need full sun and well-drained soil. Water regularly, especially during dry periods. Provide support if needed as the flowers get heavy.",
        "watering_frequency": "Water regularly, especially during dry periods.",
        "sunlight": "Full sun.",
        "fertilizing": "Moderate fertilizing during the growing season."
    },
    "tulip": {
        "scientific_name": "Tulipa spp.",
        "care_instructions": "Tulips prefer full sun to partial shade and well-drained soil. Plant bulbs in the fall. Water regularly during the growing season.  Deadhead spent flowers.",
        "watering_frequency": "Water regularly during the growing season.",
        "sunlight": "Full sun to partial shade.",
        "fertilizing": "Fertilize when planting bulbs."
    },
    "daisy": {
        "scientific_name": "Bellis perennis",
        "care_instructions": "Daisies prefer full sun and well-drained soil. Water regularly, especially during dry periods. Deadhead spent flowers to encourage more blooms.",
        "watering_frequency": "Water regularly, especially during dry periods.",
        "sunlight": "Full sun.",
        "fertilizing": "Light fertilizing during the growing season."
    },
    "orchid": {
        "scientific_name": "Orchidaceae",
        "care_instructions": "Orchids need bright, indirect light and well-draining potting mix. Water when the potting mix is almost dry. Fertilize lightly during the growing season.",
        "watering_frequency": "Water when the potting mix is almost dry.",
        "sunlight": "Bright, indirect light.",
        "fertilizing": "Fertilize lightly during the growing season."
    },
    "dandelion": { #Adding a dandelion to make sure it doesn't recognize this as a rose or sunflower
        "scientific_name": "Taraxacum officinale",
        "care_instructions": "Dandelions are hardy and adaptable.  They thrive in various conditions.", #Just a placeholder, usually they aren't 'cared' for.
        "watering_frequency": "Minimal watering needed",
        "sunlight": "Full sun to partial shade.",
        "fertilizing": "Not usually fertilized."
    }
}



# Image preprocessing function
def preprocess_image(image, target_size=(224, 224)):
    """
    Resizes and normalizes an image for use with the model.

    Args:
        image: A PIL Image object or a numpy array representing the image.
        target_size: The desired size of the image (height, width).  The model must be trained on images of this size!

    Returns:
        A numpy array representing the preprocessed image, ready for model input.
    """
    if isinstance(image, Image.Image):
        img = image.resize(target_size)  # Resize the image
        img_array = np.asarray(img)      # Convert to numpy array
    elif isinstance(image, np.ndarray):
        img = cv2.resize(image, target_size)  # Resize the image using OpenCV
        img_array = img
    else:
        raise TypeError("Unsupported image type. Provide a PIL Image or a numpy array.")

    img_array = img_array / 255.0          # Normalize pixel values to be between 0 and 1

    img_array = np.expand_dims(img_array, axis=0)  # Add a batch dimension (required by most models)
    return img_array



def identify_plant(image, model, class_names):
    """
    Identifies the plant in the given image using the provided model.

    Args:
        image: A PIL Image object or a numpy array of the image.
        model: The trained TensorFlow/Keras model for plant identification.
        class_names: A list of class names corresponding to the model's output.

    Returns:
        The name of the identified plant (string), or None if identification fails.
    """

    try:
        preprocessed_image = preprocess_image(image)
        predictions = model.predict(preprocessed_image)  # Get the model's predictions

        predicted_class_index = np.argmax(predictions[0]) # Find the index of the class with the highest probability
        predicted_class = class_names[predicted_class_index] # Get the class name

        confidence = predictions[0][predicted_class_index] #Get confidence score for the prediction.

        if confidence > 0.7: #Threshold to filter out low confidence predictions
            return predicted_class
        else:
            print(f"Low confidence: {confidence:.2f} for {predicted_class}.  Unable to confidently identify plant.")
            return None

    except Exception as e:
        print(f"Error during plant identification: {e}")
        return None



def get_plant_care_instructions(plant_name):
    """
    Retrieves care instructions for a given plant name from the mock database.

    Args:
        plant_name: The name of the plant (string).

    Returns:
        A dictionary containing the plant's scientific name and care instructions, or None if not found.
    """
    if plant_name in plant_database:
        return plant_database[plant_name]
    else:
        return None



def main():
    """
    Main function to run the plant identification program.
    """

    # Load the pre-trained model and class names
    try:
        model = load_model("plant_model.h5")  # Replace with the actual path to your model file.  Must be in h5 format.
        #The model will need to be trained first.  See additional notes at the bottom of the code.
        class_names = ['daisy', 'dandelion', 'orchid', 'rose', 'sunflower', 'tulip']  # Replace with your actual class names in the order the model predicts them.
        #These need to be in the same order as how you trained the model
    except Exception as e:
        print(f"Error loading the model: {e}")
        return  # Exit if model loading fails

    # Capture image from camera (you can replace this with an image file path)
    cap = cv2.VideoCapture(0)  # 0 is usually the default camera
    if not cap.isOpened():
        print("Error: Could not open camera.")
        return

    ret, frame = cap.read()
    if not ret:
        print("Error: Could not capture image from camera.")
        cap.release()
        return

    cap.release()
    cv2.destroyAllWindows()

    image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) #Converts frame to RGB from BGR that openCV captures and converts to Pillow Image format


    # Identify the plant in the image
    plant_name = identify_plant(image, model, class_names)


    if plant_name:
        print(f"Identified plant: {plant_name}")
        care_info = get_plant_care_instructions(plant_name)
        if care_info:
            print(f"Scientific Name: {care_info['scientific_name']}")
            print("Care Instructions:")
            print(care_info['care_instructions'])
        else:
            print("Care instructions not found for this plant.")
    else:
        print("Could not identify the plant.")


if __name__ == "__main__":
    main()


"""
Explanation of the Code:

1.  Import Libraries:
    *   `cv2`: OpenCV for image capture from the camera and basic image processing.
    *   `numpy`: For numerical operations, especially array manipulation.
    *   `PIL (Pillow)`: For image handling (opening, resizing).  This makes it easier to work with various image formats.
    *   `tensorflow.keras.models`: For loading the trained image classification model.
    *   `io.BytesIO` and `requests`:  Only needed if you plan to fetch images from URLs instead of the camera or local files.

2.  Mock Plant Database:
    *   `plant_database`: A dictionary that simulates a database to store plant information, including scientific name and care instructions.  In a real application, you'd replace this with a connection to a proper database or an external API.

3.  `preprocess_image(image, target_size)` Function:
    *   Takes an image as input (either a PIL Image object or a NumPy array, allowing flexibility).
    *   Resizes the image to the `target_size` (e.g., 224x224 pixels).  This is crucial because the image classification model expects images of a specific size.  The target size *must* match the size used during model training.
    *   Normalizes the pixel values by dividing by 255.0.  This scales the pixel values to the range [0, 1], which helps the model learn more effectively.
    *   Adds a batch dimension to the image array using `np.expand_dims(img_array, axis=0)`. Image classification models typically expect input in batches.

4. `identify_plant(image, model, class_names)` Function:

    * Takes the image, model, and class_names as inputs.
    * Preprocesses the image, and uses the model to predict the plant.
    * Returns the plant name if the confidence level is above 70%.

5.  `get_plant_care_instructions(plant_name)` Function:
    *   Looks up the plant name in the `plant_database` and returns the associated care instructions.  If the plant isn't found, it returns `None`.

6.  `main()` Function:
    *   **Loads the trained model:**  Loads the Keras/TensorFlow model from the specified file path (`plant_model.h5`). Make sure the file exists!  This file is the result of training your model (see training steps below).
    *   **Loads Class Names:** Stores the class names in a list, in the same order as what the model was trained on.
    *   **Image Capture:**
        *   Uses `cv2.VideoCapture(0)` to access the default camera (change the index if you have multiple cameras).
        *   Captures a single frame from the camera.
        *   Handles potential errors if the camera cannot be opened or an image cannot be captured.
        *   Converts the image from OpenCV's BGR format to RGB (which is often preferred by image processing libraries) and creates a PIL Image object.
    *   **Plant Identification:**
        *   Calls the `identify_plant()` function to predict the plant species in the captured image.
    *   **Display Results:**
        *   Prints the identified plant name.
        *   Retrieves care instructions using `get_plant_care_instructions()` and prints them.
        *   Handles the case where the plant cannot be identified or care instructions are not found.

7.  `if __name__ == "__main__":`
    *   Ensures that the `main()` function is executed only when the script is run directly (not when it's imported as a module).

Important Considerations and How to Train the Model:

1.  **Model Training is Essential:**  The code provided *requires* a trained image classification model (`plant_model.h5`).  It will not work without it! Here's a high-level overview of how to train the model:

    a.  **Gather a Dataset:**  Collect a large dataset of images of different plant species.  You can download existing datasets (e.g., from Kaggle) or create your own by taking pictures of plants.  Each image needs to be labeled with the correct plant species.  The more images you have, the better your model will perform.  Aim for hundreds or thousands of images *per plant species*.

    b.  **Prepare the Data:**
        *   Organize your dataset into directories, where each directory represents a different plant species (e.g., `roses`, `sunflowers`, `tulips`).
        *   Resize all images to a consistent size (e.g., 224x224 pixels). This is very important!  The `target_size` in the `preprocess_image` function *must* match the size you use during training.
        *   Split the data into training, validation, and testing sets.  The training set is used to train the model.  The validation set is used to monitor the model's performance during training and prevent overfitting. The testing set is used to evaluate the final trained model.

    c.  **Build and Train the Model:**
        *   Use TensorFlow/Keras to build a convolutional neural network (CNN). CNNs are well-suited for image classification.  You can use a pre-trained model like MobileNetV2, ResNet50, or VGG16 as a base and fine-tune it for your plant identification task. Transfer learning (using a pre-trained model) is often much faster and requires less data than training a model from scratch.
        *   Compile the model (specify the optimizer, loss function, and metrics).
        *   Train the model using the training data.  Use the validation data to monitor the model's performance.
        *   Adjust the model architecture, hyperparameters (e.g., learning rate, batch size), and training parameters as needed to improve performance.

    d.  **Evaluate and Save the Model:**
        *   Evaluate the trained model on the testing data to estimate its generalization performance.
        *   If you are satisfied with the model's performance, save it to a file (e.g., `plant_model.h5`).

    Here's a very basic example of training code (replace with your own CNN architecture):

```python
    import tensorflow as tf
    from tensorflow.keras.preprocessing.image import ImageDataGenerator
    from tensorflow.keras.models import Sequential
    from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout

    # Define image size and batch size
    img_height, img_width = 224, 224
    batch_size = 32

    # Data augmentation and preprocessing
    train_datagen = ImageDataGenerator(
        rescale=1./255,
        rotation_range=20,
        width_shift_range=0.2,
        height_shift_range=0.2,
        shear_range=0.2,
        zoom_range=0.2,
        horizontal_flip=True,
        validation_split=0.2 #Split 20% to validation
    )

    # Load data from directory
    train_generator = train_datagen.flow_from_directory(
        'path/to/your/data',  # Replace with the path to your dataset directory
        target_size=(img_height, img_width),
        batch_size=batch_size,
        class_mode='categorical', #Important to use 'categorical' for multiple classes
        subset='training' #Specify that this is the training set
    )

    validation_generator = train_datagen.flow_from_directory(
        'path/to/your/data',  # Replace with the path to your dataset directory
        target_size=(img_height, img_width),
        batch_size=batch_size,
        class_mode='categorical',
        subset='validation' #Specify that this is the validation set
    )

    # Define the model
    model = Sequential([
        Conv2D(32, (3, 3), activation='relu', input_shape=(img_height, img_width, 3)),
        MaxPooling2D((2, 2)),
        Conv2D(64, (3, 3), activation='relu'),
        MaxPooling2D((2, 2)),
        Conv2D(128, (3, 3), activation='relu'),
        MaxPooling2D((2, 2)),
        Flatten(),
        Dense(128, activation='relu'),
        Dropout(0.5),
        Dense(len(train_generator.class_indices), activation='softmax')  #Output layer, adjust number of neurons
    ])

    # Compile the model
    model.compile(optimizer='adam',
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])

    # Train the model
    epochs = 10  # Adjust as needed
    history = model.fit(
        train_generator,
        steps_per_epoch=train_generator.samples // batch_size,
        epochs=epochs,
        validation_data=validation_generator,
        validation_steps=validation_generator.samples // batch_size
    )

    # Save the model
    model.save('plant_model.h5')

    #Print the class indices to make sure the class names match the order that the model uses.
    print(train_generator.class_indices)

```

    **Important Notes on the Training Code:**

    *   **`path/to/your/data`:**  Replace this with the actual path to your dataset directory.  The directory should contain subdirectories, one for each plant species.
    *   **`target_size=(img_height, img_width)`:**  This *must* match the `target_size` used in the `preprocess_image` function in the main program.  In this example, it's 224x224.
    *   **`class_mode='categorical'`:**  Use `categorical` for multi-class classification (more than two plant species).  If you only have two species, you can use `binary`.
    *   **`Dense(len(train_generator.class_indices), activation='softmax')`:** The number of neurons in the final dense layer (`len(train_generator.class_indices)`) must match the number of plant species (classes) in your dataset.  `softmax` is used for multi-class classification.
    *   **Data Augmentation:** The `ImageDataGenerator` performs data augmentation (e.g., rotation, zooming, flipping) to artificially increase the size of your training dataset and improve the model's generalization ability. Adjust the parameters as needed.
    *   **Model Architecture:** The CNN architecture in the example is very basic.  Experiment with different architectures (more layers, different types of layers, pre-trained models) to improve performance.
    *   **Training Time:** Training a CNN can take a significant amount of time, especially on a large dataset.  Use a GPU if possible to speed up the training process.
    *   **`print(train_generator.class_indices)`**: This is crucial! It prints a dictionary that maps the subdirectory names (plant species) to the numerical index the model uses for each class.  The order of class names in the `class_names` list in your main program *must* match this order.

2.  **Class Names:** The `class_names` list in the `main()` function must contain the names of the plant species in the same order as the model's output. The training code also prints out the class names to make sure they are matching.

3.  **Error Handling:** The code includes some basic error handling (e.g., checking if the camera can be opened, handling file loading errors). Add more robust error handling as needed.

4.  **Image Acquisition:** The code captures an image from the default camera. You can modify this to load an image from a file path, URL, or other source.  If loading from a URL, use `PIL.Image.open(BytesIO(requests.get(url).content))`.

5.  **Confidence Threshold:** The `identify_plant` function includes a confidence threshold (0.7). Adjust this threshold as needed to balance accuracy and the number of successful identifications.  A higher threshold will result in fewer false positives but may also result in more failures to identify plants.

6. **Dependencies:**
    Make sure you have all the necessary libraries installed.  Use `pip install tensorflow opencv-python pillow requests`.
👁️ Viewed: 5

Comments