Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add object detection training docs #1435

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 47 additions & 3 deletions docs/source/docs/objectDetection/about-object-detection.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,55 @@ The same area, aspect ratio, and target orientation/sort parameters from {ref}`r

## Letterboxing

Photonvision will letterbox your camera frame to 640x640. This means that if you select a resolution that is larger than 640 it will be scaled down to fit inside a 640x640 frame with black bars if needed. Smaller frames will be scaled up with black bars if needed.
YOLO models must be fed a square image of the same resolution as the training dataset. To deal with this, PhotonVision will "letterbox" the camera images to 640x640. The image is first resized such that its widest dimension is 640 pixels, and then grey bars are added to pad out the rest of the image.

## Training Custom Models
## Converting a custom model

Coming soon!
After training your own YOLOv5s model, you can convert it to an RKNN file for use with PhotonVision. If you have not already trained a model, you can follow the official YOLOv5 training guide [here](https://docs.ultralytics.com/yolov5/tutorials/train_custom_data/)

### Step 1: Exporting the Model to ONNX

Using your trained YOLOv5s model, you will need to export it to ONNX format using airockchip's YOLOv5 fork.

First, download airockchip's YOLOv5 fork along with the onnx to rknn conversion script.

```bash
git clone https://github.com/airockchip/yolov5.git airockchip-yolov5
wget https://raw.githubusercontent.com/PhotonVision/photonvision/refs/heads/master/scripts/onnx2rknn.py
```

Now install the required packages, make sure you have Python 3.9 and pip downloaded.

```bash
cd airockchip-yolov5
pip install -r requirements.txt
```

#### Export Command

```bash
python3 export.py --weights '/path/to/model.pt' --rknpu --include 'onnx'
```

### Step 2: Converting ONNX to RKNN

Using the `onnx2rknn.py` script, convert the ONNX model to an RKNN file. This script was downloaded in a previous step.

#### Conversion Command

First install RKNN Toolkit:

```bash
pip install rknn-toolkit2
```

Now, run the script, passing in the ONNX model and a folder containing images from your dataset:

```bash
python3 onnx2rknn.py /path/to/model.onnx /path/to/export/model.rknn /path/to/dataset/valid/images
```

If you have any questions about the conversion process, ask in the PhotonVision Discord server.

## Uploading Custom Models

Expand Down
58 changes: 58 additions & 0 deletions photon-core/src/main/java/org/photonvision/PhotonVersion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* MIT License
*
* Copyright (c) PhotonVision
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package org.photonvision;

/*
* Autogenerated file! Do not manually edit this file. This version is regenerated
* any time the publish task is run, or when this file is deleted.
*/

import java.util.regex.Matcher;
import java.util.regex.Pattern;

@SuppressWarnings("ALL")
public final class PhotonVersion {
public static final String versionString = "dev-v2024.3.0-64-g335eeefc";
public static final String buildDate = "2024-12-10 10:56:31";
public static final boolean isRelease = !versionString.startsWith("dev");

public static final boolean versionMatches(String other) {
String c = versionString;
Pattern p = Pattern.compile("v[0-9]+.[0-9]+.[0-9]+");
Matcher m = p.matcher(c);
if (m.find()) {
c = m.group(0);
} else {
return false;
}
m = p.matcher(other);
if (m.find()) {
other = m.group(0);
} else {
return false;
}
return c.equals(other);
}
}
34 changes: 34 additions & 0 deletions photon-lib/src/generate/native/cpp/PhotonVersion.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (C) Photon Vision.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#include <string.h>
#include <regex>

/*
* Autogenerated file! Do not manually edit this file. This version is
* regenerated any time the publish task is run, or when this file is deleted.
*/

static const char* dev_ = "dev";

namespace photon {
namespace PhotonVersion {
const char* versionString = "dev-v2024.3.0-64-g335eeefc";
const char* buildDate = "2024-12-10 10:56:47";
const bool isRelease = strncmp(dev_, versionString, strlen(dev_)) != 0;
}
}
58 changes: 58 additions & 0 deletions photon-lib/src/main/java/org/photonvision/PhotonVersion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* MIT License
*
* Copyright (c) PhotonVision
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package org.photonvision;

/*
* Autogenerated file! Do not manually edit this file. This version is regenerated
* any time the publish task is run, or when this file is deleted.
*/

import java.util.regex.Matcher;
import java.util.regex.Pattern;

@SuppressWarnings("ALL")
public final class PhotonVersion {
public static final String versionString = "dev-v2024.3.0-64-g335eeefc";
public static final String buildDate = "2024-12-10 10:56:47";
public static final boolean isRelease = !versionString.startsWith("dev");

public static final boolean versionMatches(String other) {
String c = versionString;
Pattern p = Pattern.compile("v[0-9]+.[0-9]+.[0-9]+");
Matcher m = p.matcher(c);
if (m.find()) {
c = m.group(0);
} else {
return false;
}
m = p.matcher(other);
if (m.find()) {
other = m.group(0);
} else {
return false;
}
return c.equals(other);
}
}
80 changes: 80 additions & 0 deletions scripts/onnx2rknn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import argparse
import cv2
import numpy as np
from rknn.api import RKNN
import os

def collect_images_from_directory(directory_path):
"""Collect all image paths from the specified directory."""
image_extensions = ['.jpg', '.jpeg', '.png']
image_paths = []

for root, dirs, files in os.walk(directory_path):
for file in files:
if any(file.lower().endswith(ext) for ext in image_extensions):
image_paths.append(os.path.join(root, file))

return image_paths

def save_dataset_to_file(dataset, output_path):
"""Save the list of image paths to a text file."""
with open(output_path, 'w') as f:
for image_path in dataset:
f.write(image_path + '\n')
return output_path

def convert(srcFileName, dstFilename, dataset_file):
platform = "rk3588"

print('--> Source file name: ' + srcFileName)
print('--> RKNN file name: ' + dstFilename)

rknn = RKNN()

rknn.config(mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform=platform)

# Load model
print('--> Loading model')
ret = rknn.load_onnx(srcFileName)
if ret != 0:
print('load model failed!')
exit(ret)
print('done')

# Build model with quantization
print('--> Building model')
ret = rknn.build(do_quantization=True, dataset=dataset_file)
if ret != 0:
print('build model failed.')
exit(ret)
print('done')

# Export model to rknn format for Rockchip NPU
print('--> Export rknn model')
ret = rknn.export_rknn(dstFilename)
if ret != 0:
print('Export rknn model failed!')
return ret

print('export done')

rknn.release()

def main():
parser = argparse.ArgumentParser(description='Transform to RKNN model')
parser.add_argument('source_file', help='Path to the ONNX model file')
parser.add_argument('description_file', help='Output path for the RKNN model file')
parser.add_argument('quant_dir', help='Directory containing images for quantization')
args = parser.parse_args()

dataset = collect_images_from_directory(args.quant_dir)
if not dataset:
print(f"No images found in directory: {args.quant_dir}")
exit(1)

dataset_file = save_dataset_to_file(dataset, 'dataset.txt')

convert(args.source_file, args.description_file, dataset_file)

if __name__ == '__main__':
main()
Loading