ultralytics / ultralytics

NEW - YOLOv8 πŸš€ in PyTorch > ONNX > OpenVINO > CoreML > TFLite
https://docs.ultralytics.com
GNU Affero General Public License v3.0
25.96k stars 5.17k forks source link

Inquiry Regarding Cropping Objects from YOLOv8 Predictions and Saving to Folder #10356

Closed cabral0413 closed 4 weeks ago

cabral0413 commented 2 months ago

Search before asking

Question

I am currently working on an object detection project using YOLOv8, and I have successfully trained my model. To make predictions, I am using the following code snippet:

from ultralytics import YOLO import torch

model = YOLO('/best.pt')

Define path to image file

source = 'images (3).jpeg'

Run inference on the source

results = model.predict(source, show=True, save=True) # generator of Results objects

Output:

scss Copy code WARNING ⚠️ Environment does not support cv2.imshow() or PIL Image.show()

image 1/1 /content/images (3).jpeg: 448x640 515.0ms Speed: 3.1ms preprocess, 515.0ms inference, 1.7ms postprocess per image at shape (1, 3, 448, 640) Results saved to runs/obb/predict

I run above code in colab notebook.

I would like to inquire about how I can crop objects from the image using the bounding boxes generated by YOLOv8 and save them into a designated folder. Could you please provide guidance or code examples on how to achieve this?

Additional

No response

glenn-jocher commented 2 months ago

Hello! 🌟

Great to hear you've successfully trained your YOLOv8 model! To crop the objects detected by your YOLOv8 model and save them to a folder, you can use the bounding box coordinates (xyxy) present in the results. Each bounding box can be used to crop the detected object from the original image. Here's a simple example:

from ultralytics import YOLO
import cv2
import os

# Load your trained model
model = YOLO('/best.pt')

# Define path to the image file and the folder to save crops
source = 'images (3).jpeg'
save_dir = 'cropped_images'

# Ensure the save directory exists
os.makedirs(save_dir, exist_ok=True)

# Load the image
img = cv2.imread(source)

# Run inference on the source
results = model.predict(source)

# Iterate over detections
for i, det in enumerate(results[0].boxes.xyxy):
    x1, y1, x2, y2 = map(int, det[:4])
    crop_img = img[y1:y2, x1:x2]

    # Save cropped image
    crop_path = os.path.join(save_dir, f"crop_{i}.jpeg")
    cv2.imwrite(crop_path, crop_img)
    print(f"Cropped image saved to {crop_path}")

print("Cropping and saving completed!")

Make sure to replace the source and model_path with your actual image path and model path. This script will save each detected object as a separate JPEG file in the cropped_images directory.

Please adjust x1, y1, x2, y2 based on how the bounding boxes coordinates are provided in your model's results.

Happy coding! πŸš€

cabral0413 commented 2 months ago

Thank you for your quick reply and ongoing support; it is greatly appreciated.

I am currently working on an object detection project using YOLOv8 and have encountered an issue while trying to crop objects from images using bounding boxes generated by the model.

I have tried to use the code you provided to crop objects based on the bounding boxes generated by YOLOv8. Initially, I used the following code snippet to make predictions, and the model successfully generated bounding boxes around the objects in the image:

from ultralytics import YOLO

model = YOLO('/best.pt') source = 'images (3).jpeg' results = model.predict(source, show=True, save=True)

However, when I tried to crop the objects using the bounding boxes obtained from results[0].boxes.xyxy, I encountered the following error:

AttributeError: 'NoneType' object has no attribute 'xyxy'

I would greatly appreciate any guidance or code examples on how to correctly crop objects using the bounding boxes generated by YOLOv8 and save them into a designated folder. Thank you very much for your assistance.

glenn-jocher commented 2 months ago

Hi there! 🌈

Thanks for reaching out and sharing the details of the issue you encountered. It seems like the results[0].boxes object might not be initialized as expected. To ensure we're accessing the bounding box coordinates correctly, let's try a slightly adjusted approach.

First, let's make sure that results indeed contains detected objects. We can do this by printing out results or specifically checking if results[0].boxes is not None. Here's how you could adjust the code for cropping based on bounding boxes:

from ultralytics import YOLO
import cv2
import os

# Load your model and image
model = YOLO('/best.pt')
source = 'images (3).jpeg'
img = cv2.imread(source)

# Ensure results
results = model.predict(source)

if results[0].boxes is not None:  # Checking if detections were made
    for i, box in enumerate(results[0].boxes.xyxy):
        x1, y1, x2, y2 = map(int, box[:4])  # Coords
        crop_img = img[y1:y2, x1:x2]  # Cropping

        # Save directory
        save_dir = 'cropped_images'
        os.makedirs(save_dir, exist_ok=True)

        # Save cropped image
        cv2.imwrite(os.path.join(save_dir, f'crop_{i}.jpg'), crop_img)
        print(f'crop_{i}.jpg saved successfully!')
else:
    print("No objects detected.")

Make sure your model path and image path are correctly specified. This snippet checks if detection results are present, which should help avoid the 'NoneType' object has no attribute 'xyxy' error.

I hope this helps! Let us know if you need further assistance. Happy coding! πŸš€

cabral0413 commented 2 months ago

Hello 🌟

Thank you so much for your detailed response and the code snippet you provided. I really appreciate your support in trying to resolve this issue.

I tried implementing the adjusted code as per your instructions, but unfortunately, I'm still encountering an error. When running the code, it seems that no objects are being detected, which is causing the cropping process to fail. Interestingly, when I use the same image for making predictions directly without saving the crop, the detection works fine and generates bounding boxes as expected.

Here's the output I received when using the adjusted code:

image 1/1 /content/images (1).jpeg: 448x640 544.5ms Speed: 3.2ms preprocess, 544.5ms inference, 2.0ms post-process per image at shape (1, 3, 448, 640) No objects detected.

However, when running the prediction without saving the crop, it works correctly and generates bounding boxes:

WARNING ⚠️ OBB task do not support save_crop. image 1/1 /content/images (1).jpeg: 448x640 568.5ms Speed: 4.1ms preprocess, 568.5ms inference, 2.7ms postprocess per image at shape (1, 3, 448, 640) Results saved to runs/obb/predict2

I've attached the output image for your reference. 676dc1e5-1256-4ed0-a1d9-732180d9db23

Afterward, I tried checking the type of the output from results[0].boxes using the following code:

print(type(results[0].boxes)) # Check type of boxes print(results[0].boxes) # Print boxes to inspect structure The output showed:

python Copy code <class 'NoneType'> None

I want to note that despite receiving a NoneType for the bounding boxes, there are indeed objects present in the image.

Do you have any further suggestions or insights on how to address this issue? Your continued assistance is greatly appreciated!

Thank you again for your support and guidance.

glenn-jocher commented 2 months ago

@cabral0413 hello 🌟,

Thank you for the update and for providing the detailed feedback! It's quite puzzling why the detections work well when predicting directly but not when trying to access the bounding boxes for cropping. One potential explanation for this behavior could be the task-specific output format differing from the expected structure, especially noting the warning about the OBB task not supporting save_crop.

Given this, and the NoneType you're observing, it seems we might need to adjust the access method for the bounding boxes depending on the task type. Since the direct prediction works fine, let's try to ensure we're using the correct method for extracting bounding boxes based on your model's task.

Although the example provided was generally intended for detection tasks, let's add a small check to ensure compatibility with your specific YOLOv8 task, which might resolve the confusion around the output structure:

# After predict
if results[0].boxes is not None:
    for i, box in enumerate(results[0].boxes.xyxy):
        # previously shown cropping code
else:
    print("No objects detected or invalid task type for this operation.")

Additionally, considering the warning about the OBB task, ensure you're using a model compatible with the cropping functionality you're seeking. If the model is explicitly used for an oriented bounding box (OBB) task, the standard detection approach for cropping might need adjustments to accommodate the OBB output format.

If you're still facing issues, could you please share more about the model and task you're using? That might give us a clearer path to finding a solution.

Thanks for sticking with it, and looking forward to solving this together!

cabral0413 commented 2 months ago

Hello ✨✨✨

First, thank you for your quick replies and constant help; it is greatly appreciated.

To train my YOLOv8 model with the OBB task, I used the following code:

!pip install ultralytics
from IPython import display
display.clear_output()

import ultralytics
ultralytics.checks()

[output- Ultralytics YOLOv8.1.12 πŸš€ Python-3.10.12 torch-2.1.0+cu121 CUDA:0 (Tesla T4, 15102MiB) Setup complete βœ… (2 CPUs, 12.7 GB RAM, 26.3/78.2 GB disk)]

%cd {HOME}
!yolo task=obb mode=train model=yolov8s-obb.pt data={dataset.location}/data.yaml epochs=50 imgsz=640 batch=16

As per your suggestions, I also tried this:

from ultralytics import YOLO
import torch

model = YOLO('/content/drive/MyDrive/Research yolov8 obb epoch25/runs/obb/train/weights/best.pt')

# Define path to video file
source = 'images (3).jpeg'

# Run inference on the source
results = model.predict(source, show=True, save=True)  # generator of Results objects

    # After predict
if results[0].boxes is not None:
    for i, box in enumerate(results[0].boxes.xyxy):
        # previously shown cropping code
        pass
else:
    print("No objects detected or invalid task type for this operation.")

The prediction process completes without errors, and I can make predictions successfully, as evidenced by the generated bounding boxes saved in the 'runs/obb/predict' folder, but again the cropping part didn't work well.

image 1/1 /content/images (3).jpeg: 448x640 854.9ms Speed: 3.2ms preprocess, 854.9ms inference, 2.9ms postprocess per image at shape (1, 3, 448, 640) Results saved to runs/obb/predict4 No objects detected or invalid task type for this operation.

I believe the approach I am using for training and prediction is correct since the bounding boxes are being generated accurately. The issue seems to be with the cropping functionality.

I would greatly appreciate any guidance, insights, or code examples on how to resolve this error and enable successful cropping using the bounding boxes generated by YOLOv8. Thank you very much for your support and assistance.

glenn-jocher commented 2 months ago

Hey there! 🌟

Awesome to hear about your progress and thanks for sharing the detailed steps you've tried! It looks like you're on the right track with your YOLOv8 OBB model training and prediction. If the bounding boxes are correctly saved but cropping isn't working, it's likely an issue with how we're accessing or interpreting the bounding box data for OBB tasks.

For OBB models, the boxes attribute might not contain the xyxy format directly accessible for cropping as in detection models. This could explain why checking results[0].boxes is not None seems to fail for cropping, even when predictions are successful.

Given OBB models work a bit differently, let's try a slightly adjusted approach to handle the OBB bounding box format for cropping:

from ultralytics import YOLO
import cv2
import numpy as np

# Load your trained model
model = YOLO('/content/drive/MyDrive/Research yolov8 obb epoch25/runs/obb/train/weights/best.pt')

# Load the image
img = cv2.imread('images (3).jpeg')

# Run inference on the image
results = model.predict('images (3).jpeg')

# Check if we have 'xyxy' formatted boxes
if hasattr(results[0].boxes, 'xyxy'):
    for i, box in enumerate(results[0].boxes.xyxy):
        # Cropping logic here
        x1, y1, x2, y2 = map(int, box[:4])
        crop_img = img[y1:y2, x1:x2]
        print(f"Cropping and saving {i}th object")
        # Save cropped image (example path and naming)
        cv2.imwrite(f'cropped_{i}.jpg', crop_img)
else:
    print("Looks like we don't have the boxes in 'xyxy' format or no detections.")

This example assumes results[0].boxes.xyxy contains the bounding boxes. However, if OBB outputs differently (e.g., polygons for oriented boxes), you'll need to adapt the cropping logic accordingly.

If results[0].boxes doesn't directly give you what you need, consider exploring results[0].info() for clues on how to extract bounding boxes from OBB predictions or consult the YOLOv8 documentation for OBB-specific outputs.

Keep up the great work, and don't hesitate to reach out for more help! Cheers πŸš€

cabral0413 commented 2 months ago

It seems there's a common issue with the YOLOv8 model's inference using the ultralytics library, where probs and boxes are None despite objects being detected. This issue has been reported by multiple users, including myself and another user who shared the following output:


masks: None
names: {0: 'Common Krait', 1: 'Indian Cobra', 2: 'Green Vine', 3: 'Python', 4: 'Viper', 5: 'pit viper',}
obb: ultralytics.engine.results.OBB object
orig_img: array([[[215, 215, 215],
[215, 215, 215],
[215, 215, 215],
...,
[225, 225, 225],
[226, 226, 226],
[226, 226, 226]],

   [[216, 216, 216],
    [216, 216, 216],
    [216, 216, 216],
    ...,
    [223, 223, 223],
    [224, 224, 224],
    [224, 224, 224]],

   [[217, 217, 217],
    [217, 217, 217],
    [217, 217, 217],
    ...,
    [221, 221, 221],
    [221, 221, 221],
    [221, 221, 221]],

   ...,

   [[107, 107, 107],
    [106, 106, 106],
    [103, 103, 103],
    ...,
    [ 88,  88,  88],
    [ 89,  89,  89],
    [ 86,  86,  86]],

   [[115, 115, 115],
    [113, 113, 113],
    [107, 107, 107],
    ...,
    [ 87,  87,  87],
    [ 90,  90,  90],
    [ 89,  89,  89]],

   [[120, 120, 120],
    [116, 116, 116],
    [109, 109, 109],
    ...,
    [ 88,  88,  88],
    [ 94,  94,  94],
    [ 94,  94,  94]]], dtype=uint8)
orig_shape: (1600, 1200)
path: '/content/grayscale_image3.jpeg'
probs: None
save_dir: 'runs/obb/predict'
speed: {'preprocess': 7.385492324829102, 'inference': 3030.5862426757812, 'postprocess': 15.470743179321289}

This issue is causing confusion as the model can detect objects correctly but the results[0] object does not show valid probs and boxes. Could the YOLOv8 team investigate this problem further and provide any insights or solutions? Your assistance in resolving this matter would be greatly appreciated.

glenn-jocher commented 2 months ago

@cabral0413 hey there! πŸ‘‹

Thanks for bringing this to our attention. It looks like the probs and boxes attributes are returning None, which indeed seems odd given objects are being detected. This hints at a potential discrepancy in how results are interpreted, especially for the YOLOv8 model in question.

A quick tip to potentially address this issue: Please make sure you're using the latest version of the ultralytics library. Sometimes, a simple update could resolve such inconsistencies.

If the problem persists, could you try accessing the detected objects in a slightly different manner? Here’s a quick code snippet that might help reveal some underlying detections, especially for OBB tasks:

if results[0].obb:  # Check if OBB detections exist
    detections = results[0].obb.to_dict()  # Convert to dictionary if possible
    print(detections)
else:
    print("No detection data found in OBB format.")

This doesn't directly resolve the None issue for probs and boxes, but it might offer a workaround for accessing detected object data in the meantime. Also, it would be beneficial to check out results[0].info() for a comprehensive breakdown of available attributes in your results object.

We'll definitely look into this more closely on our end! πŸ•΅οΈβ€β™‚οΈ Meanwhile, if you have more details on the steps leading up to this issue or any specific configurations employed, do let us know. It'll help us investigate further and steer towards a resolution.

Appreciating your patience and support! πŸš€

cabral0413 commented 2 months ago

@glenn-jocher Hiii✨✨✨

I wanted to thank you for your continued support and consideration regarding the YOLOv8 cropping issue. I've upgraded the Ultralytics version but still got the same error.

Now I've been working on training YOLOv8 for image classification and I'm looking to integrate it with Flask to send images and receive predicted classes with probabilities. However, I encountered some issues with the provided code.


Here's a snippet of the code I'm using:
# app_with_yolo.py
from flask import Flask, render_template, request
from ultralytics import YOLO
from PIL import Image

app = Flask(__name__)

def predict_disease(image_path):
    # Load the YOLOv8 model
    model = YOLO('models/best.pt')

    # Read and resize the input image using Pillow (PIL)
    with Image.open(image_path) as img:
        img = img.resize((255, 255))

        # Convert image data to a list

    # Perform prediction on the image
    results = model(img, show=True)

    # Extract relevant information from the prediction
    names_dict = results[0].names
    probs = results[0].probs.data.tolist()
    prediction = names_dict[probs.index(max(probs))]

    return prediction

@app.route('/classify', methods=['POST'])
def predict():
    # Get the image file from the request
    file = request.files['image']

    # Save the file temporarily (optional)
    image_path = 'temp_image.jpg'
    file.save(image_path)
    # Pass the image to the YOLOv8 model for prediction
    prediction = predict_disease(image_path)

if __name__ == '__main__':
    # Run the Flask app on localhost with port 5000
    app.run(host='0.0.0.0', port=5000, debug=True)

Would it be possible for you to provide some guidance or assistance in resolving this issue? Any help or suggestions would be greatly appreciated.

Thank you once again for your support. Best regards,

glenn-jocher commented 2 months ago

Hey there! ✨

Absolutely, I'm here to help! Thanks for sticking with the upgrades and diving into image classification with YOLOv8. Integrating with Flask sounds exciting but can be a bit tricky, so let’s get you sorted.

A quick note: when using the YOLO model with Flask, we'll need to adjust how the image is preprocessed before feeding it into the model. Here’s how you could do it:

from flask import Flask, request
from ultralytics import YOLO
import numpy as np
from PIL import Image

app = Flask(__name__)
 @cabral0413.route('/classify', methods=['POST'])
def predict():
    file = request.files['image']
    img = Image.open(file.stream).convert("RGB")  # Ensure it's in RGB format

    model = YOLO('models/best.pt')  # Make sure the path is correct
    results = model(img)  # Directly pass PIL image

    # Assuming we're doing classification, let's find the top class
    probs, classes = results[0].probs.max(1)  # Get max probability and its index
    best_prob = probs[0].item()  # Best probability
    best_class = results[0].names[classes[0]]  # Class name for best probability

    return {'class': best_class, 'probability': best_prob}

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

This snippet directly uses a PIL Image object with the YOLO model without resizing, as the model itself handles resizing. Then, it finds the class with the highest probability for a simple classification setup.

If you encounter any issues or need further guidance, feel free to ask. Your projects are what keep us motivated to make YOLOv8 even better! πŸš€

Best regards!

cabral0413 commented 2 months ago

Dear @glenn-jocher ✨

I encountered an AttributeError while using the max attribute on the Probs object in YOLOv8. The error message indicated that the Probs object has no attribute max. However, I have resolved this issue by using the top1 and top1conf attributes to retrieve the index and confidence of the top class, respectively.

Here is the updated code snippet that resolves the AttributeError:


from flask import Flask, request
from ultralytics import YOLO
from PIL import Image

app = Flask(__name__)

@app.route('/classify', methods=['POST'])
def predict():
    file = request.files['image']
    img = Image.open(file.stream).convert("RGB")  # Ensure it's in RGB format

    model = YOLO('models/best.pt')  # Make sure the path is correct
    results = model(img)  # Directly pass PIL image

    # Assuming we're doing classification, let's find the top class
    top_class_idx = results[0].probs.top1  # Get index of the top class
    top_class_conf = results[0].probs.top1conf.item()  # Confidence of the top class
    top_class_name = results[0].names[top_class_idx]  # Class name for the top class

    return {'class': top_class_name, 'probability': top_class_conf}

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

I want to express my gratitude for your continued support and assistance in resolving issues. Your help has been invaluable, and I look forward to listen good news about the image cropping part in YOLOv8 OBB soon.

Thank you once again for all your support.

Best regards.

glenn-jocher commented 2 months ago

@cabral0413 hi there ✨,

Great job on resolving the AttributeError with the Probs object yourself! Your approach using the top1 and top1conf attributes looks spot-on for retrieving the top predicted class and its confidence level. It's a clever and efficient way to work with the YOLOv8 predictions πŸš€.

Your updated code snippet for integration with Flask is concise and should serve well for anyone looking to implement a similar solution. It's always fantastic to see such proactive problem-solving.

Thank you for sharing this with the community, and for your kind words! We're here to support your journey with YOLOv8, and we appreciate your contributions and insights. Stay tuned for more updates on the image cropping feature in OBB tasks.

Best regards and happy coding!

github-actions[bot] commented 1 month ago

πŸ‘‹ Hello there! We wanted to give you a friendly reminder that this issue has not had any recent activity and may be closed soon, but don't worry - you can always reopen it if needed. If you still have any questions or concerns, please feel free to let us know how we can help.

For additional resources and information, please see the links below:

Feel free to inform us of any other issues you discover or feature requests that come to mind in the future. Pull Requests (PRs) are also always welcomed!

Thank you for your contributions to YOLO πŸš€ and Vision AI ⭐