Zum Hauptinhalt springen

Script Examples

Copy these scripts into a .oneai.js file in your project folder and adapt the constants at the top. See Scripting Overview for how to create and run a script.

Capture training images​

Shows a live camera preview and saves a fixed number of images into the dataset. The dashboard closes after the last image when the script was started by the AI agent.

const TARGET_COUNT = 20;
const SPLIT = 'Train';
const PREFIX = 'part';

ui.title('Capture training images');
ui.columns(2);

const batch = time.timestamp().replace(/[^0-9]/g, '').slice(0, 14);
let captured = 0;
let requested = false;

ui.label('instructions', 'Vary the position, rotation and lighting of the part between captures.', { columnSpan: 2 });
ui.label('progress', `0 / ${TARGET_COUNT}`, { columnSpan: 2 });
await camera.preview('live', undefined, { title: 'Live camera', fps: 30, rowSpan: 2 });
ui.image('last', null, { title: 'Last capture', rowSpan: 2 });
ui.button('capture', 'Capture', () => { requested = true; }, { columnSpan: 2 });

while (app.isRunning && captured < TARGET_COUNT) {
if (!requested) {
await time.sleep(50);
continue;
}

requested = false;
ui.button('capture', 'Capturing...', () => {}, { enabled: false });

const frame = await camera.capture();
try {
const number = String(captured + 1).padStart(3, '0');
image.save(frame, `Dataset/${SPLIT}/${PREFIX}_${batch}_${number}.png`);
ui.image('last', frame, { title: `Last capture (${number})` });
} finally {
image.release(frame);
}

captured++;
ui.label('progress', `${captured} / ${TARGET_COUNT}`);
ui.button('capture', 'Capture', () => { requested = true; });
}

await camera.stopPreview('live');
log.info(`Captured ${captured} images into Dataset/${SPLIT}`);

The new images are unlabeled. Annotate them in ONE AI before you train.

Count objects in a segmentation dataset​

Counts the annotated objects per class in all samples of a split. Shows the first sample with its regions, so you can check the counting rule visually.

const SPLIT = 'Train';
const MIN_AREA = 20;

ui.title('Objects per class');

const ds = dataset.open('.');
const names = {};
for (const label of dataset.labels(ds)) names[label.id] = label.name;

const totals = {};
let samples = 0;

for (const sample of dataset.samples(ds, SPLIT)) {
if (!app.isRunning) break;
if (!sample.hasMask) continue;

const raw = dataset.loadMask(sample);
const clean = mask.open(raw, 1);
const regions = shape.components(clean, { labelNames: names, minArea: MIN_AREA });

for (const r of regions) totals[r.label] = (totals[r.label] ?? 0) + 1;
samples++;
ui.append('history', `${sample.name}: ${regions.length} objects`, { title: 'Samples' });

if (samples === 1) {
const img = dataset.loadImage(sample);
ui.image('preview', img, {
title: `First sample: ${sample.name}`,
overlays: shape.toOverlays(regions, { color: 'lime' })
});
image.release(img);
}

mask.release(clean);
mask.release(raw);
}

ui.table('perClass',
Object.entries(totals).map(([label, count]) => ({ label, count })),
{ title: `Objects in ${samples} samples`, columnSpan: 2 });

Evaluate a model on a folder of images​

Runs the newest exported model on every image in a folder and lists the results. This works for classification, object detection and segmentation models.

const FOLDER = 'Dataset/Test'; // any folder, absolute paths are allowed

ui.title('Model evaluation');

const model = onnx.latest();
if (!model) throw new Error('No ONNX model found in ./Models');
log.info(`Using ${model.name} (${model.info.modelType})`);

const rows = [];
for (const file of fs.list(FOLDER, { pattern: '*.png', filesOnly: true })) {
if (!app.isRunning) break;
if (file.name.endsWith('_seg.png')) continue; // skip segmentation masks

const img = image.load(file.path);
const p = onnx.predict(model.path, img);

let result = '';
if (p.type === 'classes') result = `${p.top.label} (${(p.top.score * 100).toFixed(1)} %)`;
else if (p.type === 'objects') result = `${p.boxes.length} objects`;
else if (p.type === 'segmentation') {
const cleaned = mask.open(p.mask, 1);
result = `${shape.count(cleaned, { minArea: 20 })} regions`;
mask.release(cleaned);
}

rows.push({ image: file.name, result });
ui.table('results', rows, { title: `${rows.length} images`, columnSpan: 2 });
image.release(img);
await time.sleep(1); // let the dashboard update
}

The example skips *_seg.png files because these are segmentation annotations, not images.

Live inspection station​

A complete inspection station with a segmentation model:

  • live camera preview and a Capture & inspect button
  • PASS/FAIL verdict with the defects drawn on the frame
  • inspection counter that persists between runs
  • CSV log and optional PDF report per inspection
  • optional result upload to an MES over HTTP
  • separate history screen
const MODEL = 'Models/model.onnx'; // exported ONE AI segmentation model
const MIN_DEFECT_AREA = 30; // smallest defect, in model output pixels
const MES_URL = ''; // e.g. 'http://mes.local/api/inspections', empty to disable

const info = onnx.info(MODEL);
if (info.modelType !== 'segmentation') throw new Error(`${MODEL} is not a segmentation model`);

// Class names from the project, every class is treated as a defect.
const names = {};
for (const label of dataset.labels(dataset.open('.'))) names[label.id] = label.name;

let requested = false;
let saveReport = false;

const VERDICT_STYLE = { width: 'half', fontSize: 32 };

function showActions(enabled) {
ui.actions('actions', [
{ text: 'Capture & inspect', onPress: () => { requested = true; }, enabled },
{ text: 'History', onPress: () => ui.showScreen('history') }
], { width: 'full' });
}

ui.title('Defect inspection');

ui.screen('inspect', { title: 'Inspection', columns: 2 });
ui.image('live', null, { title: 'Live camera', rowSpan: 2 });
ui.image('result', null, { title: 'Last result', rowSpan: 2 });
ui.label('verdict', 'Not inspected', VERDICT_STYLE);
ui.label('total', `Inspected: ${storage.get('total', 0)}`, { width: 'quarter' });
ui.checkbox('saveReport', saveReport, value => { saveReport = value; }, { title: 'Save PDF report', width: 'quarter' });
showActions(false);

ui.screen('history', { title: 'History', columns: 1 });
ui.append('history', 'Inspections of this run:');
ui.button('back', 'Back to inspection', () => ui.showScreen('inspect'));

await camera.preview('live', undefined, { fps: 30, title: 'Live camera' });
showActions(true);

while (app.isRunning) {
if (!requested) {
await time.sleep(30);
continue;
}
requested = false;
showActions(false);

// Take a fresh frame. The preview stays frozen during the inspection.
await camera.stopPreview('live');
const frame = await camera.capture();

ui.busy('result', 'Inspecting...');
await time.sleep(1);

const prediction = onnx.predict(MODEL, frame);
const cleaned = mask.open(prediction.mask, 1);
const defects = shape.components(cleaned, { labelNames: names, minArea: MIN_DEFECT_AREA });
const pass = defects.length === 0;
const verdict = pass ? 'PASS' : 'FAIL';
const overlay = mask.overlay(frame, cleaned, { opacity: 0.45 });

ui.busy('result', false);
ui.image('result', overlay, { title: `${defects.length} defects` });
ui.label('verdict', verdict, { ...VERDICT_STYLE, color: pass ? 'lime' : 'red' });

const total = storage.increment('total', 1);
ui.label('total', `Inspected: ${total}`);

const stamp = time.timestamp();
ui.append('history', `${stamp} ${verdict} ${defects.length} defects`);
fs.appendText('Results/inspections.csv', `${stamp};${verdict};${defects.length}\n`);

if (saveReport) {
const path = report.savePdf(`Results/report_${total}.pdf`, {
title: 'Defect inspection',
summary: [
{ label: 'Verdict', value: verdict },
{ label: 'Defects', value: defects.length },
{ label: 'Time', value: stamp }
],
overviewImages: [{ image: overlay, caption: 'Segmentation result' }],
findings: defects.map(d => ({
title: d.label,
verdict: 'Defect',
area: d.area,
geometry: { x: d.x, y: d.y, width: d.width, height: d.height }
}))
});
log.info(`Report saved to ${path}`);
}

if (MES_URL) {
const response = await http.post(MES_URL, { timestamp: stamp, pass, defects: defects.length });
if (!response.ok) log.warn(`MES returned HTTP ${response.status}`);
}

image.release(overlay);
image.release(frame);
mask.release(cleaned);

// Resume the live preview for the next part.
await camera.preview('live', undefined, { fps: 30, title: 'Live camera' });
showActions(true);
}

Things to adapt:

  • Defect rule: this example fails a part as soon as any region is found. Filter defects by class, area or shape to match your quality criteria. See Image Analysis & Inference.
  • Operating point: to use a confidence threshold other than 0.5, replace prediction.mask with mask.fromConfidence(prediction, undefined, { min: 0.8 }). This requires an export with confidence output.
  • MES: add the host to Settings → ONE AI → Scripting → Allowed HTTP Hosts to restrict scripts to it.
  • Permanent operation: keep Run Timeout at 0 and raise or disable the Memory Limit for stations that run all day.
  • Testing without hardware: create a camera from a recorded video with const input = camera.video('recordings/parts.mp4') and pass input.id to camera.preview and camera.capture.

To reset the inspection counter, call storage.clear() or delete the .oneai-storage.json file next to the script.