Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Conveyor Application - Barcode Detection

This example demonstrates how to detect and decode barcodes in real-time using computer vision. The application is designed for conveyor belt applications where barcodes need to be detected and decoded from video streams. It uses a [barcode detection model](https://models.luxonis.com/luxonis/barcode-detection/768x576) for detecting barcode regions and combines multiple decoding strategies (pyzbar and zxing-cpp) to ensure robust barcode recognition across various formats and conditions.

The system processes high-resolution camera input, intelligently crops detected barcode regions, and applies multiple fallback decoding strategies including rotation and color inversion to maximize recognition success rates.

## ⚠️ Important Notice

**This application will work poorly on fixed focus devices.** The barcode detection and decoding algorithms require clear, focused images to function effectively. Fixed focus cameras may struggle to capture sharp barcode images at varying distances, leading to:

- Reduced detection accuracy
- Failed barcode decoding attempts
- Inconsistent performance

For optimal results, use devices with autofocus capabilities or ensure barcodes are positioned at the camera's fixed focal distance.

## Demo

![Demo](example/barcode_demo.gif)

## Usage

Running this example requires a **Luxonis device** connected to your computer. Refer to the [documentation](https://docs.luxonis.com/software-v3/) to setup your device if you haven't done it already.

You can run the example fully on device ([`STANDALONE` mode](#standalone-mode-rvc4-only)) or using your computer as host ([`PERIPHERAL` mode](#peripheral-mode)).

Here is a list of all available parameters:

```
-d DEVICE, --device DEVICE
Optional name, DeviceID or IP of the camera to connect to. (default: None)
-fps FPS_LIMIT, --fps_limit FPS_LIMIT
FPS limit for the model runtime. (default: 10 for RVC2, 30 for RVC4)
--media_path MEDIA_PATH
Optional path to video file for processing instead of live camera feed. (default: None)
```

## Peripheral Mode

### Installation

Install libraries:

**Ubuntu:**
```bash
sudo apt-get update && apt-get install -y python3-pip libglib2.0-0 libgl1-mesa-glx wget git libzbar0 libzbar-dev
```

**macOS:**
```bash
brew install wget git zbar
```

You need to first prepare a **Python 3.10** environment with the following packages installed:

- [DepthAI](https://pypi.org/project/depthai/),
- [DepthAI Nodes](https://pypi.org/project/depthai-nodes/).

You can simply install them by running:

```bash
pip install -r requirements.txt
```

Running in peripheral mode requires a host computer and there will be communication between device and host which could affect the overall speed of the app. Below are some examples of how to run the example.

### Examples

Start the demo:
```bash
python3 main.py
```

This will run the example with default arguments.

Open visualizer on browser:
```
http://localhost:8082
```

```bash
python3 main.py --device 192.168.1.100 --fps_limit 15
```

This will connect to a specific device and set the FPS limit to 15.

```bash
python3 main.py --media_path test_video.mp4
```

This will process a video file instead of live camera feed.

## Standalone Mode (RVC4 only)

Running the example in the standalone mode, app runs entirely on the device.
To run the example in this mode, first install the `oakctl` tool using the installation instructions [here](https://docs.luxonis.com/software-v3/oak-apps/oakctl).

The app can then be run with:

```bash
oakctl connect <DEVICE_IP>
oakctl app run .
```

This will run the example with default argument values. If you want to change these values you need to edit the `oakapp.toml` file (refer [here](https://docs.luxonis.com/software-v3/oak-apps/configuration/) for more information about this configuration file).

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
103 changes: 103 additions & 0 deletions neural-networks/object-detection/conveyor-application-barcodes/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from pathlib import Path
import depthai as dai
from depthai_nodes.node import ParsingNeuralNetwork
from utils.arguments import initialize_argparser
from utils.simple_barcode_overlay import SimpleBarcodeOverlay
from utils.barcode_decoder import BarcodeDecoder

import numpy as np # ensure numpy available for perspective transform
from PIL import Image
from pyzbar.pyzbar import decode, ZBarSymbol
from PIL import ImageOps # for color inversion
import cv2 # for zxing-cpp fallback
try:
import zxingcpp # robust orientable decoder
except ImportError:
zxingcpp = None

from collections import deque
import threading
import time
import os
from datetime import datetime

from utils.host_crop_config_creator import CropConfigsCreator

DET_MODEL: str = "luxonis/barcode-detection:768x576"

_, args = initialize_argparser()

visualizer = dai.RemoteConnection(httpPort=8082)
device = dai.Device(dai.DeviceInfo(args.device)) if args.device else dai.Device()
platform = device.getPlatform().name
print(f"Platform: {platform}")

frame_type = (
dai.ImgFrame.Type.BGR888i if platform == "RVC4" else dai.ImgFrame.Type.BGR888p
)

if not args.fps_limit:
args.fps_limit = 10 if platform == "RVC2" else 30
print(
f"\nFPS limit set to {args.fps_limit} for {platform} platform. If you want to set a custom FPS limit, use the --fps_limit flag.\n"
)

with dai.Pipeline(device) as pipeline:
print("Creating pipeline...")

det_model_description = dai.NNModelDescription(DET_MODEL)
det_model_description.platform = platform
det_nn_archive = dai.NNArchive(dai.getModelFromZoo(det_model_description))


if args.media_path:
replay = pipeline.create(dai.node.ReplayVideo)
replay.setReplayVideoFile(Path(args.media_path))
replay.setOutFrameType(frame_type)
replay.setLoop(True)
if args.fps_limit:
replay.setFps(args.fps_limit)
else:
cam = pipeline.create(dai.node.Camera).build()

cam_out = cam.requestOutput((2592, 1944), frame_type, fps=args.fps_limit)
input_node = replay.out if args.media_path else cam_out

resize_node = pipeline.create(dai.node.ImageManip)
resize_node.setMaxOutputFrameSize(
det_nn_archive.getInputWidth() * det_nn_archive.getInputHeight() * 3
)
resize_node.initialConfig.setOutputSize(
det_nn_archive.getInputWidth(),
det_nn_archive.getInputHeight(),
mode=dai.ImageManipConfig.ResizeMode.STRETCH,
)
resize_node.initialConfig.setFrameType(frame_type)
input_node.link(resize_node.inputImage)

detection_nn: ParsingNeuralNetwork = pipeline.create(ParsingNeuralNetwork).build(
resize_node.out, det_nn_archive
)

crop_code = pipeline.create(CropConfigsCreator).build(
detection_nn.out,
source_size=(2592, 1944),
target_size=(640, 480),
resize_mode=dai.ImageManipConfig.ResizeMode.LETTERBOX,
)

crop_manip = pipeline.create(dai.node.ImageManip)
crop_manip.inputConfig.setReusePreviousMessage(False)
crop_manip.setMaxOutputFrameSize(640 * 480 * 5)
input_node.link(crop_manip.inputImage)
crop_code.config_output.link(crop_manip.inputConfig)

decoder = pipeline.create(BarcodeDecoder)
crop_manip.out.link(decoder.input)

# Create simple barcode overlay that shows decoded barcodes and detection boxes on video
barcode_overlay = pipeline.create(SimpleBarcodeOverlay).build(decoder.output,resize_node.out, detection_nn.out)

pipeline.run()

cv2.destroyAllWindows()
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@


identifier = "com.luxonis.conveyor-application"
app_version = "1.0.0"

prepare_container = [
{ type = "RUN", command = "apt-get update" },
{ type = "RUN", command = "apt-get install -y python3 python3-pip libglib2.0-0 libgl1-mesa-glx wget git libzbar0 libzbar-dev" },
]

prepare_build_container = []

build_steps = [
"pip3 install -r /app/requirements.txt --break-system-packages"
]

entrypoint = ["bash", "-c", "python3 -u /app/main.py"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
depthai == 3.0.0rc4
depthai-nodes
numpy
opencv-python
pyzbar
Pillow
zxing-cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from typing import List
import depthai as dai

from depthai_nodes import ImgDetectionsExtended, SECONDARY_COLOR
from depthai_nodes.utils import AnnotationHelper


class AnnotationNode(dai.node.ThreadedHostNode):
def __init__(self) -> None:
super().__init__()

self.input = self.createInput()
self.input.setPossibleDatatypes([(dai.DatatypeEnum.Buffer, True)])

self.out = self.createOutput()
self.out.setPossibleDatatypes([(dai.DatatypeEnum.Buffer, True)])

def build(
self,
gather_data_msg: dai.Node.Output,
) -> "AnnotationNode":
gather_data_msg.link(self.input)
return self

def run(self) -> None:
while self.isRunning():
gather_data_msg: dai.Buffer = self.input.get()

img_detections_extended_msg: ImgDetectionsExtended = (
gather_data_msg.reference_data
)

msg_group_list: List[dai.MessageGroup] = gather_data_msg.gathered

annotations = AnnotationHelper()

for img_detection_extended_msg, msg_group in zip(
img_detections_extended_msg.detections, msg_group_list
):
xmin, ymin, xmax, ymax = img_detection_extended_msg.rotated_rect.getOuterRect()

try:
xmin = float(xmin); ymin = float(ymin); xmax = float(xmax); ymax = float(ymax)
except Exception:
pass

xmin = max(0.0, min(1.0, xmin))
ymin = max(0.0, min(1.0, ymin))
xmax = max(0.0, min(1.0, xmax))
ymax = max(0.0, min(1.0, ymax))

annotations.draw_rectangle((xmin, ymin), (xmax, ymax))

barcode_text = ""
if "0" in msg_group:
buf_msg: dai.Buffer = msg_group["0"]
barcode_text = buf_msg.getData().decode("utf-8", errors="ignore")

if barcode_text:
annotations.draw_text(
text=barcode_text,
position=(xmin + 0.01, ymin + 0.03),
size=20,
color=SECONDARY_COLOR,
)

annotations_msg = annotations.build(
timestamp=img_detections_extended_msg.getTimestamp(),
sequence_num=img_detections_extended_msg.getSequenceNum(),
)

self.out.send(annotations_msg)
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import argparse


def initialize_argparser():
"""Initialize the argument parser for the script."""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)

parser.add_argument(
"-m",
"--model",
help="Pose model to run the inference on.",
required=False,
default="luxonis/lite-hrnet:18-coco-192x256",
type=str,
)

parser.add_argument(
"-d",
"--device",
help="Optional name, DeviceID or IP of the camera to connect to.",
required=False,
default=None,
type=str,
)

parser.add_argument(
"-fps",
"--fps_limit",
help="FPS limit for the model runtime.",
required=False,
default=None,
type=int,
)

parser.add_argument(
"-media",
"--media_path",
help="Path to the media file you aim to run the model on. If not set, the model will run on the camera input.",
required=False,
default=None,
type=str,
)
args = parser.parse_args()

return parser, args
Loading
Loading