|
|
<!--
|
|
|
CO_OP_TRANSLATOR_METADATA:
|
|
|
{
|
|
|
"original_hash": "9c4320311c0f2c1884a6a21265d98a51",
|
|
|
"translation_date": "2025-08-28T17:37:20+00:00",
|
|
|
"source_file": "5-retail/lessons/2-check-stock-device/single-board-computer-count-stock.md",
|
|
|
"language_code": "uk"
|
|
|
}
|
|
|
-->
|
|
|
# Підрахунок запасів за допомогою вашого IoT-пристрою - Віртуальне IoT обладнання та Raspberry Pi
|
|
|
|
|
|
Комбінація прогнозів і їхніх рамок може бути використана для підрахунку запасів на зображенні.
|
|
|
|
|
|
## Відображення рамок
|
|
|
|
|
|
Як корисний крок для налагодження, ви можете не лише вивести рамки на консоль, але й намалювати їх на зображенні, яке було збережене на диск під час захоплення зображення.
|
|
|
|
|
|
### Завдання - вивести рамки
|
|
|
|
|
|
1. Переконайтеся, що проект `stock-counter` відкритий у VS Code, і віртуальне середовище активоване, якщо ви використовуєте віртуальний IoT-пристрій.
|
|
|
|
|
|
1. Змініть оператор `print` у циклі `for` на наступний, щоб вивести рамки на консоль:
|
|
|
|
|
|
```python
|
|
|
print(f'{prediction.tag_name}:\t{prediction.probability * 100:.2f}%\t{prediction.bounding_box}')
|
|
|
```
|
|
|
|
|
|
1. Запустіть додаток, направивши камеру на запаси на полиці. Рамки будуть виведені на консоль із значеннями left, top, width і height у діапазоні від 0 до 1.
|
|
|
|
|
|
```output
|
|
|
pi@raspberrypi:~/stock-counter $ python3 app.py
|
|
|
tomato paste: 33.42% {'additional_properties': {}, 'left': 0.3455171, 'top': 0.09916268, 'width': 0.14175442, 'height': 0.29405564}
|
|
|
tomato paste: 34.41% {'additional_properties': {}, 'left': 0.48283678, 'top': 0.10242918, 'width': 0.11782813, 'height': 0.27467814}
|
|
|
tomato paste: 31.25% {'additional_properties': {}, 'left': 0.4923783, 'top': 0.35007596, 'width': 0.13668466, 'height': 0.28304994}
|
|
|
tomato paste: 31.05% {'additional_properties': {}, 'left': 0.36416405, 'top': 0.37494493, 'width': 0.14024884, 'height': 0.26880276}
|
|
|
```
|
|
|
|
|
|
### Завдання - намалювати рамки на зображенні
|
|
|
|
|
|
1. Пакет Pip [Pillow](https://pypi.org/project/Pillow/) можна використовувати для малювання на зображеннях. Встановіть його за допомогою наступної команди:
|
|
|
|
|
|
```sh
|
|
|
pip3 install pillow
|
|
|
```
|
|
|
|
|
|
Якщо ви використовуєте віртуальний IoT-пристрій, переконайтеся, що ви запускаєте цю команду всередині активованого віртуального середовища.
|
|
|
|
|
|
1. Додайте наступний оператор імпорту на початок файлу `app.py`:
|
|
|
|
|
|
```python
|
|
|
from PIL import Image, ImageDraw, ImageColor
|
|
|
```
|
|
|
|
|
|
Це імпортує код, необхідний для редагування зображення.
|
|
|
|
|
|
1. Додайте наступний код у кінець файлу `app.py`:
|
|
|
|
|
|
```python
|
|
|
with Image.open('image.jpg') as im:
|
|
|
draw = ImageDraw.Draw(im)
|
|
|
|
|
|
for prediction in predictions:
|
|
|
scale_left = prediction.bounding_box.left
|
|
|
scale_top = prediction.bounding_box.top
|
|
|
scale_right = prediction.bounding_box.left + prediction.bounding_box.width
|
|
|
scale_bottom = prediction.bounding_box.top + prediction.bounding_box.height
|
|
|
|
|
|
left = scale_left * im.width
|
|
|
top = scale_top * im.height
|
|
|
right = scale_right * im.width
|
|
|
bottom = scale_bottom * im.height
|
|
|
|
|
|
draw.rectangle([left, top, right, bottom], outline=ImageColor.getrgb('red'), width=2)
|
|
|
|
|
|
im.save('image.jpg')
|
|
|
```
|
|
|
|
|
|
Цей код відкриває зображення, яке було збережене раніше, для редагування. Потім він проходить через прогнози, отримуючи рамки, і обчислює координати нижнього правого кута, використовуючи значення рамок від 0 до 1. Ці значення перетворюються в координати зображення шляхом множення на відповідний розмір зображення. Наприклад, якщо значення left дорівнює 0.5 на зображенні шириною 600 пікселів, це перетвориться на 300 (0.5 x 600 = 300).
|
|
|
|
|
|
Кожна рамка малюється на зображенні червоною лінією. Нарешті, відредаговане зображення зберігається, замінюючи оригінальне зображення.
|
|
|
|
|
|
1. Запустіть додаток, направивши камеру на запаси на полиці. Ви побачите файл `image.jpg` у провіднику VS Code і зможете вибрати його, щоб побачити рамки.
|
|
|
|
|
|

|
|
|
|
|
|
## Підрахунок запасів
|
|
|
|
|
|
На зображенні, показаному вище, рамки мають невелике перекриття. Якщо це перекриття було б значно більшим, рамки могли б вказувати на один і той самий об'єкт. Щоб правильно підрахувати об'єкти, потрібно ігнорувати рамки зі значним перекриттям.
|
|
|
|
|
|
### Завдання - підрахунок запасів, ігноруючи перекриття
|
|
|
|
|
|
1. Пакет Pip [Shapely](https://pypi.org/project/Shapely/) можна використовувати для обчислення перетину. Якщо ви використовуєте Raspberry Pi, вам потрібно спочатку встановити бібліотечну залежність:
|
|
|
|
|
|
```sh
|
|
|
sudo apt install libgeos-dev
|
|
|
```
|
|
|
|
|
|
1. Встановіть пакет Shapely за допомогою Pip:
|
|
|
|
|
|
```sh
|
|
|
pip3 install shapely
|
|
|
```
|
|
|
|
|
|
Якщо ви використовуєте віртуальний IoT-пристрій, переконайтеся, що ви запускаєте цю команду всередині активованого віртуального середовища.
|
|
|
|
|
|
1. Додайте наступний оператор імпорту на початок файлу `app.py`:
|
|
|
|
|
|
```python
|
|
|
from shapely.geometry import Polygon
|
|
|
```
|
|
|
|
|
|
Це імпортує код, необхідний для створення багатокутників для обчислення перекриття.
|
|
|
|
|
|
1. Над кодом, який малює рамки, додайте наступний код:
|
|
|
|
|
|
```python
|
|
|
overlap_threshold = 0.20
|
|
|
```
|
|
|
|
|
|
Це визначає відсоток перекриття, який дозволений, перш ніж рамки вважаються одним і тим самим об'єктом. Значення 0.20 визначає 20% перекриття.
|
|
|
|
|
|
1. Для обчислення перекриття за допомогою Shapely рамки потрібно перетворити в багатокутники Shapely. Додайте наступну функцію для цього:
|
|
|
|
|
|
```python
|
|
|
def create_polygon(prediction):
|
|
|
scale_left = prediction.bounding_box.left
|
|
|
scale_top = prediction.bounding_box.top
|
|
|
scale_right = prediction.bounding_box.left + prediction.bounding_box.width
|
|
|
scale_bottom = prediction.bounding_box.top + prediction.bounding_box.height
|
|
|
|
|
|
return Polygon([(scale_left, scale_top), (scale_right, scale_top), (scale_right, scale_bottom), (scale_left, scale_bottom)])
|
|
|
```
|
|
|
|
|
|
Ця функція створює багатокутник, використовуючи рамку прогнозу.
|
|
|
|
|
|
1. Логіка для видалення перекритих об'єктів включає порівняння всіх рамок, і якщо будь-які пари прогнозів мають рамки, які перекриваються більше, ніж поріг, один із прогнозів видаляється. Для порівняння всіх прогнозів ви порівнюєте прогноз 1 з 2, 3, 4 тощо, потім 2 з 3, 4 тощо. Наступний код виконує це:
|
|
|
|
|
|
```python
|
|
|
to_delete = []
|
|
|
|
|
|
for i in range(0, len(predictions)):
|
|
|
polygon_1 = create_polygon(predictions[i])
|
|
|
|
|
|
for j in range(i+1, len(predictions)):
|
|
|
polygon_2 = create_polygon(predictions[j])
|
|
|
overlap = polygon_1.intersection(polygon_2).area
|
|
|
|
|
|
smallest_area = min(polygon_1.area, polygon_2.area)
|
|
|
|
|
|
if overlap > (overlap_threshold * smallest_area):
|
|
|
to_delete.append(predictions[i])
|
|
|
break
|
|
|
|
|
|
for d in to_delete:
|
|
|
predictions.remove(d)
|
|
|
|
|
|
print(f'Counted {len(predictions)} stock items')
|
|
|
```
|
|
|
|
|
|
Перекриття обчислюється за допомогою методу Shapely `Polygon.intersection`, який повертає багатокутник із перекриттям. Площа обчислюється з цього багатокутника. Цей поріг перекриття не є абсолютним значенням, а має бути відсотком від рамки, тому знаходиться найменша рамка, і поріг перекриття використовується для обчислення площі, яку може мати перекриття, щоб не перевищити відсотковий поріг перекриття найменшої рамки. Якщо перекриття перевищує це значення, прогноз позначається для видалення.
|
|
|
|
|
|
Як тільки прогноз позначений для видалення, його більше не потрібно перевіряти, тому внутрішній цикл переривається, щоб перевірити наступний прогноз. Ви не можете видаляти елементи зі списку під час його ітерації, тому рамки, які перекриваються більше, ніж поріг, додаються до списку `to_delete`, а потім видаляються в кінці.
|
|
|
|
|
|
Нарешті, кількість запасів виводиться на консоль. Це потім може бути відправлено до IoT-сервісу для сповіщення, якщо рівень запасів низький. Увесь цей код знаходиться перед малюванням рамок, тому ви побачите прогнози запасів без перекриттів на створених зображеннях.
|
|
|
|
|
|
> 💁 Це дуже спрощений спосіб видалення перекриттів, просто видаляючи перший із пари, що перекривається. Для виробничого коду вам потрібно було б додати більше логіки, наприклад, враховуючи перекриття між кількома об'єктами або якщо одна рамка міститься в іншій.
|
|
|
|
|
|
1. Запустіть додаток, направивши камеру на запаси на полиці. Вихідні дані покажуть кількість рамок без перекриттів, які перевищують поріг. Спробуйте змінити значення `overlap_threshold`, щоб побачити, як прогнози ігноруються.
|
|
|
|
|
|
> 💁 Ви можете знайти цей код у папці [code-count/pi](../../../../../5-retail/lessons/2-check-stock-device/code-count/pi) або [code-count/virtual-iot-device](../../../../../5-retail/lessons/2-check-stock-device/code-count/virtual-iot-device).
|
|
|
|
|
|
😀 Ваш додаток для підрахунку запасів був успішним!
|
|
|
|
|
|
---
|
|
|
|
|
|
**Відмова від відповідальності**:
|
|
|
Цей документ був перекладений за допомогою сервісу автоматичного перекладу [Co-op Translator](https://github.com/Azure/co-op-translator). Хоча ми прагнемо до точності, будь ласка, майте на увазі, що автоматичні переклади можуть містити помилки або неточності. Оригінальний документ на його рідній мові слід вважати авторитетним джерелом. Для критичної інформації рекомендується професійний людський переклад. Ми не несемо відповідальності за будь-які непорозуміння або неправильні тлумачення, що виникають внаслідок використання цього перекладу. |