TensorFlow Lite и Android Things - найти обнаруженный объект и сохранить его в объектах RectF? - PullRequest
0 голосов
/ 22 ноября 2018

У меня есть планшет Android, на котором я установил TensorFlow-Lite DetectorActivity в доступных примерах.Он хорошо работает на планшете Android.Однако, когда я попытался развернуть его на RaspberryPi 3 Model B, которая работала на Android Things, он не работал.Казалось, есть проблема с правильной настройкой камеры с точки зрения включения предварительного просмотра камеры в реальном времени и проведения анализа.

Моя первоначальная цель - запустить приложение обнаружения объектов на Android Things.Также важно нарисовать ограничивающий прямоугольник на обнаруженных объектах.

Я искал пример приложения для Android, которое использовало TensorFlow-Lite и работало на Android Things.Я быстро нашел этот пример из hackster.io, который использует классификацию изображений для распределения конфет .Я запустил его на своей плате RaspberryPi, и он запустился.Он дает результаты, название объекта, уровень достоверности, а также идентификатор.Я был в порядке, чтобы основываться на этом примере кода.Вместо прямой трансляции с камеры я мог просто заставить приложение делать фотографии, анализировать и давать результат.После чего он делает еще одну фотографию и цикл продолжается.

Однако это не указывало местоположение в терминах объекта RectF.

Я пытался адаптировать recognizeFunction в примере TFLite для Android, он находится в классе TFLiteObjectDetectionAPIModel,Я адаптировал его к функции doIdentification приложения Candy Dispenser для Android.Моя функция теперь выглядит следующим образом:

// outputLocations: array of shape [Batchsize, NUM_DETECTIONS,4]
// contains the location of detected boxes
private float[][][] outputLocations;
// outputClasses: array of shape [Batchsize, NUM_DETECTIONS]
// contains the classes of detected boxes
private float[][] outputClasses;
// outputScores: array of shape [Batchsize, NUM_DETECTIONS]
// contains the scores of detected boxes
private float[][] outputScores;
// numDetections: array of shape [Batchsize]
// contains the number of detected boxes
private float[] numDetections;

private static final int NUM_DETECTIONS = 10;

private static final float IMAGE_MEAN = 128.0f;
private static final float IMAGE_STD = 128.0f;



private void doIdentification(Bitmap image) {
    Log.e(TAG, "doing identification!");
    Trace.beginSection("recognizeImage");

    int numBytesPerChannel;
    if (TF_OD_API_IS_QUANTIZED) {
        Log.e(TAG, "model is quantized");
        numBytesPerChannel = 1; // Quantized
    } else {
        Log.e(TAG, "model is NOT quantized");
        numBytesPerChannel = 4; // Floating point
    }


    ByteBuffer imgData = ByteBuffer.allocateDirect(1 * TF_INPUT_IMAGE_HEIGHT * TF_INPUT_IMAGE_HEIGHT
            * 3 * numBytesPerChannel);

    Trace.beginSection("preprocessBitmap");
    // Preprocess the image data from 0-255 int to normalized float based
    // on the provided parameters.
    int[] intValues = new int[TF_INPUT_IMAGE_HEIGHT * TF_INPUT_IMAGE_HEIGHT];

    image.getPixels(intValues, 0, image.getWidth(), 0, 0, image.getWidth(), image.getHeight());

    imgData.rewind();
    for (int i = 0; i < TF_INPUT_IMAGE_HEIGHT; ++i) {
        for (int j = 0; j < TF_INPUT_IMAGE_HEIGHT; ++j) {
            int pixelValue = intValues[i * TF_INPUT_IMAGE_HEIGHT + j];

            if (TF_OD_API_IS_QUANTIZED) {
                imgData.put((byte) ((pixelValue >> 16) & 0xFF));
                imgData.put((byte) ((pixelValue >> 8) & 0xFF));
                imgData.put((byte) (pixelValue & 0xFF));
            } else {
                imgData.putFloat((((pixelValue >> 16) & 0xFF) - IMAGE_MEAN) / IMAGE_STD);
                imgData.putFloat((((pixelValue >> 8) & 0xFF) - IMAGE_MEAN) / IMAGE_STD);
                imgData.putFloat(((pixelValue & 0xFF) - IMAGE_MEAN) / IMAGE_STD);
            }

        }
    }
    Trace.endSection(); // preprocessBitmap


    // Allocate space for the inference results
    byte[][] confidencePerLabel = new byte[1][mLabels.size()];


    //for box detections
    // Copy the input data into TensorFlow.
    Trace.beginSection("feed");
    outputLocations = new float[1][NUM_DETECTIONS][4];
    outputClasses = new float[1][NUM_DETECTIONS];
    outputScores = new float[1][NUM_DETECTIONS];
    numDetections = new float[1];

    Object[] inputArray = {imgData};
    Map<Integer, Object> outputMap = new HashMap<>();
    outputMap.put(0, outputLocations);
    outputMap.put(1, outputClasses);
    outputMap.put(2, outputScores);
    outputMap.put(3, numDetections);
    Trace.endSection();


    // Read image data into buffer formatted for the TensorFlow model
    TensorFlowHelper.convertBitmapToByteBuffer(image, intValues, imgData);

    // Run inference on the network with the image bytes in imgData as input,
    // storing results on the confidencePerLabel array.

    Trace.beginSection("run");
    mTensorFlowLite.runForMultipleInputsOutputs(inputArray, outputMap);
    Trace.endSection();

    // TODO - we try and fetch our rectF's here

    final ArrayList<Recognition> recognitions = new ArrayList<>(NUM_DETECTIONS);
    for (int i = 0; i < NUM_DETECTIONS; ++i) {
        final RectF detection =
                new RectF(
                        outputLocations[0][i][1] * TF_OD_API_INPUT_SIZE,
                        outputLocations[0][i][0] * TF_OD_API_INPUT_SIZE,
                        outputLocations[0][i][3] * TF_OD_API_INPUT_SIZE,
                        outputLocations[0][i][2] * TF_OD_API_INPUT_SIZE);
        // SSD Mobilenet V1 Model assumes class 0 is background class
        // in label file and class labels start from 1 to number_of_classes+1,
        // while outputClasses correspond to class index from 0 to number_of_classes
        int labelOffset = 1;

        Log.e(TAG, "adding the following to our results: ");
        Log.e(TAG, "recognition id: " + i);
        Log.e(TAG, "recognition label: " + mLabels.get((int) outputClasses[0][i] + labelOffset));
        Log.e(TAG, "recognition confidence: " + outputScores[0][i]);

        recognitions.add(
                new Recognition(
                        "" + i,
                        mLabels.get((int) outputClasses[0][i] + labelOffset),
                        outputScores[0][i],
                        detection));

    }
    Trace.endSection(); // "recognizeImage"


        // TODO -- This is the old working code
        // Get the results with the highest confidence and map them to their labels
        Collection<Recognition> results = TensorFlowHelper.getBestResults(confidencePerLabel, mLabels);
        Log.e(TAG, "results count is = " + results.size());

        // Report the results with the highest confidence
        onClassificationComplete(results);

}

Я установил для квантованной константы значение true и запустил код.Тем не менее, меня приветствовала следующая ошибка:

java.lang.IllegalArgumentException: Cannot convert between a TensorFlowLite tensor with type UINT8 and a Java object of type [[[F (which is compatible with the TensorFlowLite type FLOAT32).

и ответственная строка:

mTensorFlowLite.runForMultipleInputsOutputs(inputArray, outputMap);

Я попытался перейти на mTensorFlowLite.run, но это привело к другомуошибка.

Кто-нибудь реализовывал обнаружение объектов (рисование RectF на обнаруженных объектах) на Android Things?

1 Ответ

0 голосов
/ 27 ноября 2018

Проблема здесь, вероятно, в том, что вы смешиваете модели.Существует разница между классификацией изображений и обнаружением объектов моделями.Классификация просто сообщает о достоверности определенного типа объекта в изображении, а обнаружение добавляет при определении местоположения объекта.Образец дозатора конфет, с которого вы начинаете, использует модель классификации изображений (mobilenet_quant_v1_224.tflite), тогда как упомянутый вами образец TFLite использует модель обнаружения объекта (mobilenet_ssd.tflite).

Я бы рекомендовал начинать с образца, который выполняетобнаружение объектов и проработка проблем с камерой, а не решение проблемы наоборот.Образец диспенсера конфет (а также официальный образец классификатора изображений ) являются хорошим справочным материалом для получения камерой RPi3 для захвата изображения и преобразования его для использования с моделью.

...