8.6 KiB
Liczenie zapasów za pomocą urządzenia IoT - Wio Terminal
Połączenie przewidywań i ich ramki ograniczającej może być użyte do liczenia zapasów na obrazie.
Liczenie zapasów
Na powyższym obrazie ramki ograniczające mają niewielkie nakładanie się. Jeśli to nakładanie byłoby znacznie większe, ramki mogłyby wskazywać na ten sam obiekt. Aby poprawnie policzyć obiekty, należy ignorować ramki z istotnym nakładaniem się.
Zadanie - liczenie zapasów ignorując nakładanie się
-
Otwórz swój projekt
stock-counter
, jeśli nie jest już otwarty. -
Nad funkcją
processPredictions
dodaj następujący kod:const float overlap_threshold = 0.20f;
Ten kod definiuje procent nakładania się, który jest akceptowalny, zanim ramki ograniczające zostaną uznane za ten sam obiekt. 0.20 oznacza 20% nakładania się.
-
Poniżej tego, ale nadal nad funkcją
processPredictions
, dodaj następujący kod do obliczenia nakładania się między dwoma prostokątami:struct Point { float x, y; }; struct Rect { Point topLeft, bottomRight; }; float area(Rect rect) { return abs(rect.bottomRight.x - rect.topLeft.x) * abs(rect.bottomRight.y - rect.topLeft.y); } float overlappingArea(Rect rect1, Rect rect2) { float left = max(rect1.topLeft.x, rect2.topLeft.x); float right = min(rect1.bottomRight.x, rect2.bottomRight.x); float top = max(rect1.topLeft.y, rect2.topLeft.y); float bottom = min(rect1.bottomRight.y, rect2.bottomRight.y); if ( right > left && bottom > top ) { return (right-left)*(bottom-top); } return 0.0f; }
Ten kod definiuje strukturę
Point
do przechowywania punktów na obrazie oraz strukturęRect
do definiowania prostokąta za pomocą współrzędnych górnego lewego i dolnego prawego rogu. Następnie definiuje funkcjęarea
, która oblicza powierzchnię prostokąta na podstawie współrzędnych górnego lewego i dolnego prawego rogu.Kolejno definiuje funkcję
overlappingArea
, która oblicza powierzchnię nakładania się dwóch prostokątów. Jeśli się nie nakładają, zwraca 0. -
Poniżej funkcji
overlappingArea
zadeklaruj funkcję do konwersji ramki ograniczającej naRect
:Rect rectFromBoundingBox(JsonVariant prediction) { JsonObject bounding_box = prediction["boundingBox"].as<JsonObject>(); float left = bounding_box["left"].as<float>(); float top = bounding_box["top"].as<float>(); float width = bounding_box["width"].as<float>(); float height = bounding_box["height"].as<float>(); Point topLeft = {left, top}; Point bottomRight = {left + width, top + height}; return {topLeft, bottomRight}; }
Funkcja ta pobiera przewidywanie z detektora obiektów, wyodrębnia ramkę ograniczającą i używa wartości z ramki do zdefiniowania prostokąta. Prawa strona jest obliczana jako lewa plus szerokość. Dolna jako górna plus wysokość.
-
Przewidywania muszą być porównane ze sobą, a jeśli dwa przewidywania mają nakładanie się większe niż próg, jedno z nich musi zostać usunięte. Próg nakładania się jest procentem, więc musi być pomnożony przez rozmiar najmniejszej ramki ograniczającej, aby sprawdzić, czy nakładanie się przekracza określony procent ramki, a nie procent całego obrazu. Zacznij od usunięcia zawartości funkcji
processPredictions
. -
Dodaj następujący kod do pustej funkcji
processPredictions
:std::vector<JsonVariant> passed_predictions; for (int i = 0; i < predictions.size(); ++i) { Rect prediction_1_rect = rectFromBoundingBox(predictions[i]); float prediction_1_area = area(prediction_1_rect); bool passed = true; for (int j = i + 1; j < predictions.size(); ++j) { Rect prediction_2_rect = rectFromBoundingBox(predictions[j]); float prediction_2_area = area(prediction_2_rect); float overlap = overlappingArea(prediction_1_rect, prediction_2_rect); float smallest_area = min(prediction_1_area, prediction_2_area); if (overlap > (overlap_threshold * smallest_area)) { passed = false; break; } } if (passed) { passed_predictions.push_back(predictions[i]); } }
Ten kod deklaruje wektor do przechowywania przewidywań, które się nie nakładają. Następnie przechodzi przez wszystkie przewidywania, tworząc
Rect
z ramki ograniczającej.Następnie kod przechodzi przez pozostałe przewidywania, zaczynając od tego, które jest po bieżącym przewidywaniu. Zapobiega to wielokrotnemu porównywaniu przewidywań - gdy 1 i 2 zostały porównane, nie ma potrzeby porównywania 2 z 1, tylko z 3, 4 itd.
Dla każdej pary przewidywań obliczana jest powierzchnia nakładania się. Następnie jest ona porównywana z powierzchnią najmniejszej ramki ograniczającej - jeśli nakładanie się przekracza próg procentowy najmniejszej ramki, przewidywanie jest oznaczane jako niezaliczone. Jeśli po porównaniu wszystkich nakładań przewidywanie przechodzi testy, jest dodawane do kolekcji
passed_predictions
.💁 To bardzo uproszczony sposób usuwania nakładań, po prostu usuwając pierwsze z pary nakładających się. W kodzie produkcyjnym warto dodać więcej logiki, na przykład uwzględniając nakładania się między wieloma obiektami lub sytuację, gdy jedna ramka ograniczająca jest zawarta w innej.
-
Po tym dodaj następujący kod do wysyłania szczegółów zaliczonych przewidywań do monitora szeregowego:
for(JsonVariant prediction : passed_predictions) { String boundingBox = prediction["boundingBox"].as<String>(); String tag = prediction["tagName"].as<String>(); float probability = prediction["probability"].as<float>(); char buff[32]; sprintf(buff, "%s:\t%.2f%%\t%s", tag.c_str(), probability * 100.0, boundingBox.c_str()); Serial.println(buff); }
Ten kod przechodzi przez zaliczone przewidywania i drukuje ich szczegóły na monitorze szeregowym.
-
Poniżej tego dodaj kod do drukowania liczby policzonych przedmiotów na monitorze szeregowym:
Serial.print("Counted "); Serial.print(passed_predictions.size()); Serial.println(" stock items.");
Liczba ta może być następnie wysłana do usługi IoT, aby ostrzec, jeśli poziomy zapasów są niskie.
-
Prześlij i uruchom swój kod. Skieruj kamerę na przedmioty na półce i naciśnij przycisk C. Spróbuj dostosować wartość
overlap_threshold
, aby zobaczyć ignorowane przewidywania.Connecting to WiFi.. Connected! Image captured Image read to buffer with length 17416 tomato paste: 35.84% {"left":0.395631,"top":0.215897,"width":0.180768,"height":0.359364} tomato paste: 35.87% {"left":0.378554,"top":0.583012,"width":0.14824,"height":0.359382} tomato paste: 34.11% {"left":0.699024,"top":0.592617,"width":0.124411,"height":0.350456} tomato paste: 35.16% {"left":0.513006,"top":0.647853,"width":0.187472,"height":0.325817} Counted 4 stock items.
💁 Ten kod znajdziesz w folderze code-count/wio-terminal.
😀 Twój program do liczenia zapasów zakończył się sukcesem!
Zastrzeżenie:
Ten dokument został przetłumaczony za pomocą usługi tłumaczenia AI Co-op Translator. Chociaż dokładamy wszelkich starań, aby tłumaczenie było precyzyjne, prosimy pamiętać, że automatyczne tłumaczenia mogą zawierać błędy lub nieścisłości. Oryginalny dokument w jego rodzimym języku powinien być uznawany za wiarygodne źródło. W przypadku informacji o kluczowym znaczeniu zaleca się skorzystanie z profesjonalnego tłumaczenia wykonanego przez człowieka. Nie ponosimy odpowiedzialności za jakiekolwiek nieporozumienia lub błędne interpretacje wynikające z użycia tego tłumaczenia.