Использование обнаружения объектов YOLOv8 на Android (шаги + код)
Использование обнаружения объектов YOLOv8 на Android (шаги + код)

Визуальная/изображительная информация, доставленная в кратчайшие сроки!

Предисловие

Yolov8 — популярный ИИ для обнаружения объектов. Android — самая популярная мобильная операционная система в мире.

В этой статье описывается, как выполнить обнаружение объектов yolov8 на устройствах Android.

Шаг 1. Конвертируйте формат Pytorch в формат tflite.

YOLOv8 построен в формате pytorch. Конвертируйте его в tflite для использования на Android.

Установить YOLOv8

Установите фреймворк под названием Ultralytics. Yolov8 включен в эту структуру.

Язык кода Слово:javascript
копировать
pip install ultralytics

Конвертировать в тфлайт

Преобразование с использованием кодов преобразования. Следующий код загрузит веса предварительно обученной модели.

Если у вас есть файл контрольных точек веса для модели, обученной с использованием ваших собственных данных, замените раздел yolov8s.pt.

Язык кода Слово:javascript
копировать
from ultralytics import YOLO
model = YOLO('yolov8s.pt')
model.export(format="tflite")

yolov8s_saved_model/yolov8s_float16.tflite будет создан, поэтому используйте его.

Если произошла ошибка преобразования...

Если возникает следующая ошибка, это связано с версией tensorflow, поэтому установите совместимую версию.

ImportError: generic_type: невозможно инициализировать тип «StatusCode»: объект с таким именем уже определен

Например, измените tensorflow на следующую версию.

Язык кода Слово:javascript
копировать
pip install tensorflow==2.13.0

Запуск файлов tflite на Android

Отсюда мы запустим файл yolov8 tflite в проекте студии Android.

Добавьте файл tflite в проект

Создайте каталог ресурсов (Файл → Создать → Папка → Папка активов) в каталоге приложения проекта студии Android и добавьте файл tflite (yolov8s_float32.tflite) и labels.txt, которые можно добавить путем копирования и вставки.

labels.txt — это текстовый файл, описывающий имя класса модели YOLOv8, как показано ниже.

Если вы установили собственный класс, напишите этот класс.

Предварительно обученная модель YOLOv8 по умолчанию выглядит следующим образом.

Содержимое файла labels.txt следующее:

Язык кода Слово:javascript
копировать
person
bicycle
car
motorcycle
airplane
bus
train
truck
boat
traffic light
fire hydrant
stop sign
parking meter
bench
bird
cat
dog
horse
sheep
cow
elephant
bear
zebra
giraffe
backpack
umbrella
handbag
tie
suitcase
frisbee
skis
snowboard
sports ball
kite
baseball bat
baseball glove
skateboard
surfboard
tennis racket
bottle
wine glass
cup
fork
knife
spoon
bowl
banana
apple
sandwich
orange
broccoli
carrot
hot dog
pizza
donut
cake
chair
couch
potted plant
bed
dining table
toilet
tv
laptop
mouse
remote
keyboard
cell phone
microwave
oven
toaster
sink
refrigerator
book
clock
vase
scissors
teddy bear
hair drier
toothbrush

Установить тфлайт

Добавьте следующее в app/build.gradle.kts Зависимости в к Установить тфлайт рамка.

приложение/build.gradle.kts

Язык кода Слово:javascript
копировать
implementation("org.tensorflow:tensorflow-lite:2.14.0")
implementation("org.tensorflow:tensorflow-lite-support:0.4.4")

После добавления вышеуказанного контента нажмите «Синхронизировать сейчас», чтобы установить.

Импортируйте необходимые модули

Язык кода Слово:javascript
копировать
import org.tensorflow.lite.DataType
import org.tensorflow.lite.Interpreter
import org.tensorflow.lite.gpu.CompatibilityList
import org.tensorflow.lite.gpu.GpuDelegate
import org.tensorflow.lite.support.common.FileUtil
import org.tensorflow.lite.support.common.ops.CastOp
import org.tensorflow.lite.support.common.ops.NormalizeOp
import org.tensorflow.lite.support.image.ImageProcessor
import org.tensorflow.lite.support.image.TensorImage
import org.tensorflow.lite.support.tensorbuffer.TensorBuffer
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader

необходимые атрибуты класса

Язык кода Слово:javascript
копировать
private val modelPath = "yolov8s_float32.tflite"
private val labelPath = "labels.txt"
private var interpreter: Interpreter? = null
private var tensorWidth = 0
private var tensorHeight = 0
private var numChannel = 0
private var numElements = 0
private var labels = mutableListOf<String>()
private val imageProcessor = ImageProcessor.Builder()
    .add(NormalizeOp(INPUT_MEAN, INPUT_STANDARD_DEVIATION))
    .add(CastOp(INPUT_IMAGE_TYPE))
    .build() // preprocess input

companion object {
    private const val INPUT_MEAN = 0f
    private const val INPUT_STANDARD_DEVIATION = 255f
    private val INPUT_IMAGE_TYPE = DataType.FLOAT32
    private val OUTPUT_IMAGE_TYPE = DataType.FLOAT32
    private const val CONFIDENCE_THRESHOLD = 0.3F
    private const val IOU_THRESHOLD = 0.5F
}

Инициализировать модель

Инициализируйте модель tflite. Получите файл модели и передайте его интерпретатору tflite. При необходимости передайте количество используемых потоков.

Если вы используете его в классе, отличном от Activity, вам необходимо передать контекст этому классу.

Язык кода Слово:javascript
копировать
val model = FileUtil.loadMappedFile(context, modelPath)
val options = Interpreter.Options()
options.numThreads = 4
interpreter = Interpreter(model, options)

Получите входные и выходные формы yolov8 из интерпретатора.

Язык кода Слово:javascript
копировать
val inputShape = interpreter.getInputTensor(0).shape()
val outputShape = interpreter.getOutputTensor(0).shape()

tensorWidth = inputShape[1]
tensorHeight = inputShape[2]
numChannel = outputShape[1]
numElements = outputShape[2]

Прочтите имя класса из файла label.txt.

InputStream и InputStreamReader должны быть закрыты явно.

Язык кода Слово:javascript
копировать
try {
    val inputStream: InputStream = context.assets.open(labelPath)
    val reader = BufferedReader(InputStreamReader(inputStream))
    var line: String? = reader.readLine()
    while (line != null && line != "") {
        labels.add(line)
        line = reader.readLine()
    }
    reader.close()
    inputStream.close()
} catch (e: IOException) {
    e.printStackTrace()
}

Введите изображение и выполните

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

1. Измените размер, чтобы он соответствовал входной форме модели.

2. Сделайте это тензором

3. Нормализуйте значение пикселя, разделив его на 255 (придав ему значение в диапазоне от 0 до 1).

4. Преобразование во входной тип модели.

5. Введите, чтобы получить imageBuffer

Язык кода Слово:javascript
копировать
val resizedBitmap = Bitmap.createScaledBitmap(bitmap, tensorWidth, tensorHeight, false)
val tensorImage = TensorImage(DataType.FLOAT32)
tensorImage.load(resizedBitmap)
val processedImage = imageProcessor.process(tensorImage)
val imageBuffer = processedImage.buffer

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

Язык кода Слово:javascript
копировать
val output = TensorBuffer.createFixedSize(intArrayOf(1 , numChannel, numElements), OUTPUT_IMAGE_TYPE)
interpreter.run(imageBuffer, output.buffer)

Постобработка вывода

Поле вывода рассматривается как класс BoudingBox.

Это класс с классом, коробкой и уровнем доверия.

x1, y1 — начальная точка. x2, y2 — конечные точки. cx, cy — центры. w — ширина, h — высота.

Язык кода Слово:javascript
копировать
data class BoundingBox(
    val x1: Float,
    val y1: Float,
    val x2: Float,
    val y2: Float,
    val cx: Float,
    val cy: Float,
    val w: Float,
    val h: Float,
    val cnf: Float,
    val cls: Int,
    val clsName: String
)

Следующий процесс — выбрать более надежный блок из множества кандидатов на выходной блок.

1. Извлеките поля, достоверность которых превышает порог достоверности.

2. Среди перекрывающихся коробок сохранить коробку с наибольшей надежностью. (нмс)

Язык кода Слово:javascript
копировать
private fun bestBox(array: FloatArray) : List<BoundingBox>? {

    val boundingBoxes = mutableListOf<BoundingBox>()

    for (c in 0 until numElements) {
        var maxConf = -1.0f
        var maxIdx = -1
        var j = 4
        var arrayIdx = c + numElements * j
        while (j < numChannel){
            if (array[arrayIdx] > maxConf) {
                maxConf = array[arrayIdx]
                maxIdx = j - 4
            }
            j++
            arrayIdx += numElements
        }

        if (maxConf > CONFIDENCE_THRESHOLD) {
            val clsName = labels[maxIdx]
            val cx = array[c] // 0
            val cy = array[c + numElements] // 1
            val w = array[c + numElements * 2]
            val h = array[c + numElements * 3]
            val x1 = cx - (w/2F)
            val y1 = cy - (h/2F)
            val x2 = cx + (w/2F)
            val y2 = cy + (h/2F)
            if (x1 < 0F || x1 > 1F) continue
            if (y1 < 0F || y1 > 1F) continue
            if (x2 < 0F || x2 > 1F) continue
            if (y2 < 0F || y2 > 1F) continue

            boundingBoxes.add(
                BoundingBox(
                    x1 = x1, y1 = y1, x2 = x2, y2 = y2,
                    cx = cx, cy = cy, w = w, h = h,
                    cnf = maxConf, cls = maxIdx, clsName = clsName
                )
            )
        }
    }

    if (boundingBoxes.isEmpty()) return null

    return applyNMS(boundingBoxes)
}

private fun applyNMS(boxes: List<BoundingBox>) : MutableList<BoundingBox> {
    val sortedBoxes = boxes.sortedByDescending { it.cnf }.toMutableList()
    val selectedBoxes = mutableListOf<BoundingBox>()

    while(sortedBoxes.isNotEmpty()) {
        val first = sortedBoxes.first()
        selectedBoxes.add(first)
        sortedBoxes.remove(first)

        val iterator = sortedBoxes.iterator()
        while (iterator.hasNext()) {
            val nextBox = iterator.next()
            val iou = calculateIoU(first, nextBox)
            if (iou >= IOU_THRESHOLD) {
                iterator.remove()
            }
        }
    }

    return selectedBoxes
}

private fun calculateIoU(box1: BoundingBox, box2: BoundingBox): Float {
    val x1 = maxOf(box1.x1, box2.x1)
    val y1 = maxOf(box1.y1, box2.y1)
    val x2 = minOf(box1.x2, box2.x2)
    val y2 = minOf(box1.y2, box2.y2)
    val intersectionArea = maxOf(0F, x2 - x1) * maxOf(0F, y2 - y1)
    val box1Area = box1.w * box1.h
    val box2Area = box2.w * box2.h
    return intersectionArea / (box1Area + box2Area - intersectionArea)
}

На этом этапе вы получите выходные данные yolov8.

Язык кода Слово:javascript
копировать
val bestBoxes = bestBox(output.floatArray)

Нарисуйте поле вывода на изображении

Язык кода Слово:javascript
копировать
fun drawBoundingBoxes(bitmap: Bitmap, boxes: List<BoundingBox>): Bitmap {
    val mutableBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true)
    val canvas = Canvas(mutableBitmap)
    val paint = Paint().apply {
        color = Color.RED
        style = Paint.Style.STROKE
        strokeWidth = 8f
    }
    val textPaint = Paint().apply {
        color = Color.WHITE
        textSize = 40f
        typeface = Typeface.DEFAULT_BOLD
    }

    for (box in boxes) {
        val rect = RectF(
            box.x1 * mutableBitmap.width,
            box.y1 * mutableBitmap.height,
            box.x2 * mutableBitmap.width,
            box.y2 * mutableBitmap.height
        )
        canvas.drawRect(rect, paint)
        canvas.drawText(box.clsName, rect.left, rect.bottom, textPaint)
    }

    return mutableBitmap
}

В некоторых случаях путь к модели требуется, когда интерпретатор пуст.

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose