Skip to content

Commit

Permalink
Add example pipeline
Browse files Browse the repository at this point in the history
Co-authored-by: Dolly Chen <[email protected]>
  • Loading branch information
ruaridhg and dollychen committed Nov 8, 2024
1 parent f4b2938 commit c44fd94
Show file tree
Hide file tree
Showing 5 changed files with 541 additions and 0 deletions.
12 changes: 12 additions & 0 deletions example_pipeline/colon.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@


# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
train: /colon_data/polyps_small/images/train
val: /colon_data/polyps_small/images/valid
test: /colon_data/polyps_small/images/valid # 20288 of 40670 images, submit to https://competitions.codalab.org/competitions/20794

# number of classes
nc: 1

# class names
names: [ 'polyp']
228 changes: 228 additions & 0 deletions example_pipeline/find_bounding_box.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Number of items: 2\n",
"Item 1: x_min=0, y_min=0, x_max=100, y_max=105\n",
"Item 2: x_min=144, y_min=225, x_max=193, y_max=278\n"
]
}
],
"source": [
"import cv2\n",
"import numpy as np\n",
"\n",
"def find_connected_components(image_path):\n",
" # Read the image\n",
" image = cv2.imread(image_path)\n",
" \n",
" # Convert the image to grayscale\n",
" gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
" \n",
" # Threshold the image to create a binary image\n",
" _, binary = cv2.threshold(gray, 1, 255, cv2.THRESH_BINARY)\n",
" \n",
" # Find connected components\n",
" num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(binary, connectivity=8)\n",
" \n",
" # Initialize a list to store bounding boxes\n",
" bounding_boxes = []\n",
" \n",
" for i in range(1, num_labels): # Skip the background label 0\n",
" x_min = stats[i, cv2.CC_STAT_LEFT]\n",
" y_min = stats[i, cv2.CC_STAT_TOP]\n",
" width = stats[i, cv2.CC_STAT_WIDTH]\n",
" height = stats[i, cv2.CC_STAT_HEIGHT]\n",
" x_max = x_min + width - 1\n",
" y_max = y_min + height - 1\n",
" \n",
" bounding_boxes.append((x_min, y_min, x_max, y_max))\n",
" \n",
" return num_labels - 1, bounding_boxes # Subtract 1 to exclude the background\n",
"\n",
"# Example usage\n",
"image_path = '../images/tst_seg_tst_seg500.png'\n",
"num_items, bounding_boxes = find_connected_components(image_path)\n",
"print(f\"Number of items: {num_items}\")\n",
"for i, (x_min, y_min, x_max, y_max) in enumerate(bounding_boxes):\n",
" print(f\"Item {i+1}: x_min={x_min}, y_min={y_min}, x_max={x_max}, y_max={y_max}\")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"ename": "SyntaxError",
"evalue": "invalid syntax (2760322440.py, line 44)",
"output_type": "error",
"traceback": [
"\u001b[1;36m Cell \u001b[1;32mIn[3], line 44\u001b[1;36m\u001b[0m\n\u001b[1;33m num_items, boundinimport cv2\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid syntax\n"
]
}
],
"source": [
"import cv2\n",
"import numpy as np\n",
"\n",
"def find_connected_components(image_path, output_file):\n",
" # Read the image\n",
" image = cv2.imread(image_path)\n",
" \n",
" # Convert the image to grayscale\n",
" gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
" \n",
" # Threshold the image to create a binary image\n",
" _, binary = cv2.threshold(gray, 1, 255, cv2.THRESH_BINARY)\n",
" \n",
" # Find connected components\n",
" num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(binary, connectivity=8)\n",
" \n",
" # Initialize a list to store bounding boxes\n",
" bounding_boxes = []\n",
" \n",
" for i in range(1, num_labels): # Skip the background label 0\n",
" x_min = stats[i, cv2.CC_STAT_LEFT]\n",
" y_min = stats[i, cv2.CC_STAT_TOP]\n",
" width = stats[i, cv2.CC_STAT_WIDTH]\n",
" height = stats[i, cv2.CC_STAT_HEIGHT]\n",
" x_max = x_min + width - 1\n",
" y_max = y_min + height - 1\n",
" \n",
" # Convert to center coordinates and dimensions\n",
" x_center = x_min + width / 2\n",
" y_center = y_min + height / 2\n",
" \n",
" bounding_boxes.append((x_center, y_center, width, height))\n",
" \n",
" # Write results to a text file\n",
" with open(output_file, 'w') as f:\n",
" for x_center, y_center, width, height in bounding_boxes:\n",
" f.write(f\"0 {x_center:.8f} {y_center:.8f} {width:.8f} {height:.8f}\\n\")\n",
" \n",
" return num_labels - 1, bounding_boxes # Subtract 1 to exclude the background\n",
"\n",
"# Example usage\n",
"image_path = '../images/tst_seg_tst_seg500.png'\n",
"output_file = '../labels/bounding_boxes.txt'\n",
"num_items, bounding_boxes = find_connected_components(image_path, output_file)\n",
"print(f\"Number of items: {num_items}\")\n",
"for i, (x_center, y_center, width, height) in enumerate(bounding_boxes):\n",
" print(f\"Item {i+1}: x_center={x_center}, y_center={y_center}, width={width}, height={height}\")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Number of items: 2\n",
"Item 1: x_center=0.09115523465703972, y_center=0.11830357142857142, width=0.18231046931407943, height=0.23660714285714285\n",
"Item 2: x_center=0.30505415162454874, y_center=0.5625, width=0.09025270758122744, height=0.12053571428571429\n"
]
}
],
"source": [
"import cv2\n",
"import numpy as np\n",
"import os\n",
"\n",
"def find_connected_components(image_path, output_dir):\n",
" # Read the image\n",
" image = cv2.imread(image_path)\n",
" \n",
" # Get image dimensions\n",
" image_height, image_width = image.shape[:2]\n",
" \n",
" # Convert the image to grayscale\n",
" gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
" \n",
" # Threshold the image to create a binary image\n",
" _, binary = cv2.threshold(gray, 1, 255, cv2.THRESH_BINARY)\n",
" \n",
" # Find connected components\n",
" num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(binary, connectivity=8)\n",
" \n",
" # Initialize a list to store bounding boxes\n",
" bounding_boxes = []\n",
" \n",
" for i in range(1, num_labels): # Skip the background label 0\n",
" x_min = stats[i, cv2.CC_STAT_LEFT]\n",
" y_min = stats[i, cv2.CC_STAT_TOP]\n",
" width = stats[i, cv2.CC_STAT_WIDTH]\n",
" height = stats[i, cv2.CC_STAT_HEIGHT]\n",
" \n",
" # Convert to center coordinates and dimensions\n",
" x_center = x_min + width / 2\n",
" y_center = y_min + height / 2\n",
" \n",
" # Convert to ratios\n",
" x_center /= image_width\n",
" y_center /= image_height\n",
" width /= image_width\n",
" height /= image_height\n",
" \n",
" bounding_boxes.append((x_center, y_center, width, height))\n",
" \n",
" # Create the output file path\n",
" base_name = os.path.basename(image_path)\n",
" output_file = os.path.join(output_dir, os.path.splitext(base_name)[0] + '.txt')\n",
" \n",
" # Write results to a text file\n",
" with open(output_file, 'w') as f:\n",
" for x_center, y_center, width, height in bounding_boxes:\n",
" f.write(f\"0 {x_center:.8f} {y_center:.8f} {width:.8f} {height:.8f}\\n\")\n",
" \n",
" return num_labels - 1, bounding_boxes # Subtract 1 to exclude the background\n",
"\n",
"# Example usage\n",
"image_path = '../images/tst_seg_tst_seg500.png'\n",
"output_dir = '../labels'\n",
"num_items, bounding_boxes = find_connected_components(image_path, output_dir)\n",
"print(f\"Number of items: {num_items}\")\n",
"for i, (x_center, y_center, width, height) in enumerate(bounding_boxes):\n",
" print(f\"Item {i+1}: x_center={x_center}, y_center={y_center}, width={width}, height={height}\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "pgta",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.10"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
62 changes: 62 additions & 0 deletions example_pipeline/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
This is an example pipeline on how to convert output from Blender Randomiser to COCO format labelling in preperation of fine tuning polyps detection using YOLOv7

# Data Preperation
Once the segmentation of polyps are obtained. Use find_bounding_box.ipynb to output the bounding box information as text files.

show_bbox.ipynb can be used to show if the correct bounding boxes have been converted.

# IMPORTANT!
**<span style="color:red">
When Google Colab session finishes, ALL data is wiped, please save the models!
</span>**
## To tune the model using custom data:
- clone yolov7 git and install it https://github.com/WongKinYiu/yolov7; pip install -r requirements.txt
- log into wandb account (your own API - see below) DO NOT SHARE!
- run python training script

```python train.py --epochs 100 --device 0 --entity colon_coders --workers 8 --batch-size 32 --data /content/colon.yaml --img 512 512 --cfg /content/yolov7_training_config.yaml --weights '/content/yolov7_training.pt' --name yolov7-colon --hyp data/hyp.scratch.custom.yaml```

## Google colab instructions
- upload polyps.zip to google drive
- upload colon.yaml, yolov7_training.pt, yolov7_training_config.yaml to google drive
- open google colab and mount drive
- unzip polyps.zip
```python
import zipfile
with zipfile.ZipFile("/content/drive/MyDrive/polyps.zip", 'r') as zip_ref:
zip_ref.extractall("/content/colon_data")
```
- Important: remove the cache files (otherwise the model will use the cache file to load the data which has the incorrect file paths)
- could use to code in show_bbox.ipynb to see if data and bounding boxes has been loaded correctly
- install yolo7
```python
!git clone https://github.com/WongKinYiu/yolov7
%cd yolov7
!pip install -r requirements.txt
```
- set up wandb
```python
!pip install wandb
import wandb
wandb.login()
```
- tune model: make sure colon.yaml has the correct file paths for data, also make sure --data, --cfg and --weights has the correct file paths

```python train.py --epochs 100 --device 0 --entity colon_coders --workers 8 --batch-size 32 --data /content/colon.yaml --img 512 512 --cfg /content/yolov7_training_config.yaml --weights '/content/yolov7_training.pt' --name yolov7-colon --hyp data/hyp.scratch.custom.yaml```
- When training is finished, model output is saved under yolov7/runs/train

## Run on test data
!python test.py --data /content/colon.yaml --img 512 --batch 32 --conf 0.001 --iou 0.65 --device 0 --weights runs/train/yolov7-colon2/weights/best.pt --name yolov7_colon_val

## Notes
- The data location is specified in the config file colon.yaml
- Training config is specified in yolov7_training_config.yaml

## Weights and Biases
Weights and Biases is a very good tool to use to track training progress. YoloV7 uses this tool and it is very easy to set up
- https://wandb.ai/site/research sign up for the free account
- log into your account, go to top right, under your name select "user profile"
- go to section "danger zone" and reveal your API code
- this code is then used to log in to wandb when prompted
- when you finish you can change the API or throw it away.
- when training is in progress, go to WandB website, click on top left, you should see the project YOLO which will show the current training session
99 changes: 99 additions & 0 deletions example_pipeline/show_bbox.ipynb

Large diffs are not rendered by default.

Loading

0 comments on commit c44fd94

Please sign in to comment.