File size: 3,285 Bytes
b72c906
a76bb3d
 
 
 
 
 
 
 
 
 
 
 
9d31a14
976fba6
a76bb3d
 
9d31a14
180a7d9
9d31a14
 
180a7d9
9d31a14
a76bb3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75ef24f
 
a76bb3d
9d31a14
422e9a8
 
a76bb3d
75ef24f
 
a4b10d7
75ef24f
 
b5d1004
b8a9903
75ef24f
b72c906
75ef24f
9d31a14
b8a9903
75ef24f
9d31a14
 
a76bb3d
9d31a14
 
 
a76bb3d
 
 
9d31a14
 
 
a76bb3d
 
 
 
 
 
 
 
9d31a14
 
 
 
a76bb3d
 
 
 
ccfd95d
a76bb3d
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import { env, AutoProcessor, AutoModel, RawImage } from 'https://cdn.jsdelivr.net/npm/@xenova/transformers@2.15.1';

// Since we will download the model from the Hugging Face Hub, we can skip the local model check
env.allowLocalModels = false;

// Reference the elements that we will need
const status = document.getElementById('status');
const fileUpload = document.getElementById('upload');
const imageContainer = document.getElementById('container');
const example = document.getElementById('example');

const EXAMPLE_URL = 'https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/city-streets.jpg';

const THRESHOLD = 0.25;

// Create a new object detection pipeline
status.textContent = 'Loading model...';
const processor = await AutoProcessor.from_pretrained('Xenova/yolov9-c_all');

// For this demo, we resize the image so that its shortest edge is 256px
processor.feature_extractor.size = { shortest_edge: 256 }

const model = await AutoModel.from_pretrained('Xenova/yolov9-c_all');
status.textContent = 'Ready';

example.addEventListener('click', (e) => {
    e.preventDefault();
    detect(EXAMPLE_URL);
});

fileUpload.addEventListener('change', function (e) {
    const file = e.target.files[0];
    if (!file) {
        return;
    }

    const reader = new FileReader();

    // Set up a callback when the file is loaded
    reader.onload = e2 => detect(e2.target.result);

    reader.readAsDataURL(file);
});


// Detect objects in the image
async function detect(url) {
    // Update UI
    imageContainer.innerHTML = '';

    // Read image
    const image = await RawImage.fromURL(url);

    // Set container width and height depending on the image aspect ratio
    const ar = image.width / image.height;
    const [cw, ch] = (ar > 1) ? [640, 640 / ar] : [640 * ar, 640];
    imageContainer.style.width = `${cw}px`;
    imageContainer.style.height = `${ch}px`;
    imageContainer.style.backgroundImage = `url(${url})`;

    status.textContent = 'Analysing...';

    // Preprocess image
    const inputs = await processor(image);

    // Predict bounding boxes
    const { outputs } = await model(inputs);

    status.textContent = '';

    const sizes = inputs.reshaped_input_sizes[0].reverse();
    outputs.tolist().forEach(x => renderBox(x, sizes));
}

// Render a bounding box and label on the image
function renderBox([xmin, ymin, xmax, ymax, score, id], [w, h]) {
    if (score < THRESHOLD) return; // Skip boxes with low confidence

    // Generate a random color for the box
    const color = '#' + Math.floor(Math.random() * 0xFFFFFF).toString(16).padStart(6, 0);

    // Draw the box
    const boxElement = document.createElement('div');
    boxElement.className = 'bounding-box';
    Object.assign(boxElement.style, {
        borderColor: color,
        left: 100 * xmin / w + '%',
        top: 100 * ymin / h + '%',
        width: 100 * (xmax - xmin) / w + '%',
        height: 100 * (ymax - ymin) / h + '%',
    })

    // Draw label
    const labelElement = document.createElement('span');
    labelElement.textContent = model.config.id2label[id];
    labelElement.className = 'bounding-box-label';
    labelElement.style.backgroundColor = color;

    boxElement.appendChild(labelElement);
    imageContainer.appendChild(boxElement);
}