как рисовать полигоны на изображении с помощью Vue/JavaScript

Вопрос или проблема

У меня есть изображение с коробками и кодами на каждой коробке. Я отправляю его в Google Vision и получаю ответ. Затем я отображаю список кнопок для каждого “кода продукта”. При нажатии на кнопку он должен использовать “код продукта”, чтобы найти его координаты из списка ответов и нарисовать многоугольник на изображении, чтобы выделить коробку с этим “кодом продукта”. Я следую документации mdn о том, как рисовать многоугольники, но это не работает. Пожалуйста, помогите.

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.5.4/vue.global.min.js"></script>
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <title>Сканирование</title>
    <style>
        .form-group {
            margin-bottom: 1rem;
        }

        .code-button {
            margin: 5px;
        }
    </style>
</head>
<body>
    <div id="scan" class="container">
        <div class="row">
            <div class="col-md-12">
                <h1>Сканирование</h1>
                <div class="form-group">
                    <label for="image">Изображение</label>
                    <input type="file" accept="image/*" class="form-control" id="image" name="image" @change="handleFileUpload">
                </div>
                <div class="form-group">
                    <button v-if="isImageSelected" class="btn btn-warning" @click="restartImage">Перезапустить изображение</button>
                    <button class="btn btn-success" @click="sendImage">Отправить</button>
                </div>
                <div style="position: relative; width: 640px; height: 480px;">
                    <canvas id="canvas" width="640" height="480" style="position: absolute; top: 0; left: 0; z-index: 1;"></canvas>
                    <img id="preview" src="" alt="Предварительный просмотр" class="img-fluid" style="position: absolute; top: 0; left: 0; z-index: 0;">
                </div>
                <div id="results"></div>
                <div id="code-buttons">
                    <button v-for="code in codeList" :key="code" class="code-button" @click="highlightBox(code)">
                        [[ code ]]
                    </button>
                </div>
            </div>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous"></script>

    <script>
        const response = {
            "product_codes": [
                {
                    "code": "697902",
                    "bounding_poly": [
                        [574, 1250],
                        [766, 1256],
                        [765, 1282],
                        [573, 1276]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [762, 1390],
                        [960, 1384],
                        [961, 1413],
                        [763, 1419]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [673, 1570],
                        [880, 1579],
                        [878, 1612],
                        [672, 1603]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [364, 1792],
                        [573, 1796],
                        [572, 1825],
                        [364, 1821]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [640, 1796],
                        [848, 1779],
                        [851, 1811],
                        [643, 1828]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [1066, 1390],
                        [1248, 1388],
                        [1248, 1412],
                        [1066, 1414]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [942, 1784],
                        [1150, 1770],
                        [1152, 1800],
                        [944, 1814]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [631, 2039],
                        [848, 2024],
                        [850, 2053],
                        [633, 2068]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [1231, 1807],
                        [1445, 1809],
                        [1445, 1840],
                        [1231, 1838]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [1221, 2047],
                        [1439, 2032],
                        [1442, 2069],
                        [1224, 2084]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [591, 2295],
                        [811, 2275],
                        [814, 2309],
                        [594, 2329]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [904, 2296],
                        [1126, 2294],
                        [1126, 2327],
                        [904, 2329]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [216, 2572],
                        [444, 2543],
                        [449, 2581],
                        [221, 2611]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [561, 2558],
                        [788, 2546],
                        [790, 2582],
                        [563, 2594]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [897, 2559],
                        [1119, 2547],
                        [1121, 2586],
                        [899, 2598]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [2159, 2223],
                        [2384, 2237],
                        [2381, 2271],
                        [2157, 2257]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [3293, 2021],
                        [3513, 2024],
                        [3513, 2056],
                        [3293, 2053]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [3411, 2289],
                        [3641, 2289],
                        [3641, 2321],
                        [3411, 2321]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [2133, 2490],
                        [2361, 2509],
                        [2358, 2543],
                        [2130, 2525]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [3801, 2508],
                        [4030, 2503],
                        [4031, 2537],
                        [3802, 2542]
                    ]
                },
                {
                    "code": "697902",
                    "bounding_poly": [
                        [3464, 2534],
                        [3698, 2541],
                        [3697, 2577],
                        [3463, 2570]
                    ]
                }
            ]
        };

        const { createApp } = Vue;

        createApp({
            delimiters: ['[[', ']]'],
            data() {
                return {
                    canvas: null,
                    context: null,
                    isImageSelected: false,
                    capturedImage: null,
                    response: response,
                    codeList: [...new Set(response.product_codes.map(item => item.code))]
                }
            },
            methods: {
                handleFileUpload(event) {
                    const file = event.target.files[0];
                    const reader = new FileReader();
                    reader.onload = (e) => {
                        this.capturedImage = e.target.result;
                        document.getElementById('preview').src = this.capturedImage;
                        document.getElementById('preview').style.display = 'block';
                        this.isImageSelected = true;
                    };
                    reader.readAsDataURL(file);
                },
                sendImage() {
                    this.drawPolygons();
                },
                restartImage() {
                    // Сбросить состояния
                    this.isImageSelected = false;
                    document.getElementById('preview').src="";
                    document.getElementById('preview').style.display = 'none';
                    document.getElementById('results').innerHTML = '';
                    this.capturedImage = null;
                },
                drawPolygons() {
                    const canvas = document.getElementById('canvas');
                    const ctx = canvas.getContext('2d');
                    if (!ctx) {
                        console.error('Не удалось получить контекст канваса');
                        return;
                    }
                    const preview = document.getElementById('preview');
                    canvas.width = preview.width;
                    canvas.height = preview.height;
                    canvas.style.display = 'block'; // Убедиться, что канвас видим

                    this.response.product_codes.forEach(item => {
                        this.drawPolygon(item.bounding_poly, 'rgba(0, 255, 0, 0.5)');
                    });
                },
                drawPolygon(points, color) {
                    const canvas = document.getElementById('canvas');
                    const ctx = canvas.getContext('2d');
                    if (!ctx) {
                        console.error('Не удалось получить контекст канваса');
                        return;
                    }
                    ctx.fillStyle = color;
                    ctx.beginPath();
                    ctx.moveTo(points[0][0], points[0][1]);
                    points.slice(1).forEach(point => {
                        ctx.lineTo(point[0], point[1]);
                    });
                    ctx.closePath();
                    ctx.fill();
                },
                highlightBox(code) {
                    console.log('Выделяем коробку для кода:', code);
                    const canvas = document.getElementById('canvas');
                    const ctx = canvas.getContext('2d');
                    if (!ctx) {
                        console.error('Не удалось получить контекст канваса');
                        return;
                    }
                    ctx.clearRect(0, 0, canvas.width, canvas.height);
                    this.drawPolygons();

                    const pointsArray = this.response.product_codes
                        .filter(item => item.code === code)
                        .map(item => item.bounding_poly);
                    console.log('Массив точек:', pointsArray);
                    pointsArray.forEach(points => {
                        this.drawPolygon(points, 'rgba(255, 0, 0, 0.5)');
                    });
                }
            }
        }).mount('#scan');
    </script>
</body>
</html>

.

Ответ или решение

Как нарисовать полигоны на изображении с использованием Vue и JavaScript

Работа с изображениями и их аннотацией — это важная задача в современных приложениях на JavaScript. В вашем случае требуется нарисовать полигоны на загруженной картинке на основе координат, полученных от Google Vision API. Давайте разберем, как это можно сделать с использованием Vue.js и стандартного HTML5 Canvas.

Шаг за шагом: реализуем функцию рисования полигонов

Шаг 1: Подготовка HTML и Vue

Во-первых, необходимо создать HTML-разметку и подключить Vue.js. Ваша разметка уже содержит элемент <canvas>, который будет использоваться для рисования полигонов, и элемент <img>, который будет отображать загруженное изображение.

<div id="scan" class="container">
    <div style="position: relative; width: 640px; height: 480px;">
        <canvas id="canvas" width="640" height="480" style="position: absolute; top: 0; left: 0; z-index: 1;"></canvas>
        <img id="preview" src="" alt="Preview" class="img-fluid" style="position: absolute; top: 0; left: 0; z-index: 0;">
    </div>
    <div id="code-buttons">
        <button v-for="code in codeList" :key="code" @click="highlightBox(code)">
            [[ code ]]
        </button>
    </div>
</div>

Шаг 2: Обработка загрузки изображения

Для работы с изображением создайте метод handleFileUpload, который будет загружать изображение и отображать его на элементе <img>. Также вы проверяете, выбрано ли изображение, чтобы активировать соответствующую кнопку.

handleFileUpload(event) {
    const file = event.target.files[0];
    const reader = new FileReader();
    reader.onload = (e) => {
        this.capturedImage = e.target.result;
        document.getElementById('preview').src = this.capturedImage;
        this.isImageSelected = true;
    };
    reader.readAsDataURL(file);
}

Шаг 3: Рисование полигонов

Для рисования полигонов вам потребуется метод drawPolygons, который будет вызываться при загрузке изображения и при выборе конкретного кода продукта.

drawPolygons() {
    const ctx = document.getElementById('canvas').getContext('2d');
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // очищаем канвас

    this.response.product_codes.forEach(item => {
        this.drawPolygon(item.bounding_poly, 'rgba(0, 255, 0, 0.5)');
    });
}

Метод drawPolygon будет принимать массив координат и цвет для рисования:

drawPolygon(points, color) {
    const ctx = document.getElementById('canvas').getContext('2d');
    ctx.fillStyle = color;
    ctx.beginPath();
    ctx.moveTo(points[0][0], points[0][1]);

    points.slice(1).forEach(point => {
        ctx.lineTo(point[0], point[1]);
    });
    ctx.closePath();
    ctx.fill();
}

Шаг 4: Выделение конкретного прямоугольника

Когда пользователь нажимает на кнопку с определенным кодом, необходимо выделить соответствующий прямоугольник. Для этого вызывайте метод highlightBox, который будет фильтровать координаты по коду продукта и заново рисовать только этот полигон.

highlightBox(code) {
    const ctx = document.getElementById('canvas').getContext('2d');
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // очищаем канвас
    this.drawPolygons();

    const pointsArray = this.response.product_codes
        .filter(item => item.code === code)
        .map(item => item.bounding_poly);

    pointsArray.forEach(points => {
        this.drawPolygon(points, 'rgba(255, 0, 0, 0.5)'); // измените цвет полигона для выделения
    });
}

Шаг 5: Подключение Vue и стилизация

С помощью Vue мы связываем все эти методы, чтобы они работали вместе и создавали интуитивно понятный интерфейс для пользователя. Не забудьте добавить некоторые стили для вашего канваса, чтобы он смотрелся аккуратно и современно.

Заключение

Вышеуказанные шаги помогут вам эффективно нарисовать полигоны на изображении с использованием Vue.js и JavaScript. Убедитесь, что вы обрабатываете все события правильно и предоставляете пользователю удобный интерфейс для взаимодействия с загруженной картинкой. Реализация описанных методов позволит вам не только рисовать полигоны, но и взаимодействовать с пользователем, выделяя нужные элементы. Эффективное использование Canvas в сочетании с Vue.js открывает большие возможности для разработки интерактивных приложений.

Оцените материал
Добавить комментарий

Капча загружается...