Классификация изображений через Tensorflow дает точно такой же прогноз - PullRequest
0 голосов
/ 01 октября 2018

Терпите меня, пока я пытаюсь объяснить.

У меня есть приложение для Android, которое использует OpenCV для преобразования изображения YUV420 в растровое изображение и передачи его интерпретатору.Проблема в том, что каждый раз, когда я запускаю его, я получаю одно и то же предсказание класса с точно такими же значениями достоверности, не зависящими от того, на что я указываю.

...
Recognitions : [macbook pro: 0.95353276, cello gripper: 0.023749515]. 
Recognitions : [macbook pro: 0.95353276, cello gripper: 0.023749515]. 
Recognitions : [macbook pro: 0.95353276, cello gripper: 0.023749515]. 
Recognitions : [macbook pro: 0.95353276, cello gripper: 0.023749515].
...

Теперь, прежде чем упомянуть, моя модель недостаточно обучена,Я протестировал точно такой же файл .tflite в примере TFLite, представленном в Tensorflow Codelab-2.Он работает так, как должен, и распознает все 4 моих класса с точностью до 90%.Кроме того, я использовал сценарий label_image.py для проверки файла .pb, с помощью которого был извлечен мой .tflite, и он работает как надо.Я тренировал модель на более чем 5000 изображений каждого класса.Поскольку он работает в других приложениях, я полагаю, что с моделью нет проблем, кроме моей реализации.Хотя я просто не могу точно определить это.

Следующий код используется для создания матов из байтов изображения:

//Retrieve the camera Image from ARCore
val cameraImage = frame.acquireCameraImage()
val cameraPlaneY = cameraImage.planes[0].buffer
val cameraPlaneUV = cameraImage.planes[1].buffer

// Create a new Mat with OpenCV. One for each plane - Y and UV
val y_mat = Mat(cameraImage.height, cameraImage.width, CvType.CV_8UC1, cameraPlaneY)
val uv_mat = Mat(cameraImage.height / 2, cameraImage.width / 2, CvType.CV_8UC2, cameraPlaneUV)
var mat224 = Mat()
var cvFrameRGBA = Mat()

// Retrieve an RGBA frame from the produced YUV
Imgproc.cvtColorTwoPlane(y_mat, uv_mat, cvFrameRGBA, Imgproc.COLOR_YUV2BGRA_NV21)
// I've tried the following in the above line
// Imgproc.COLOR_YUV2RGBA_NV12
// Imgproc.COLOR_YUV2RGBA_NV21
// Imgproc.COLOR_YUV2BGRA_NV12
// Imgproc.COLOR_YUV2BGRA_NV21

Следующий код используется для добавления данных изображения в ByteBuffer:

// imageFrame is a Mat object created from OpenCV by processing a YUV420 image received from ARCore
override fun setImageFrame(imageFrame: Mat) {
    ...
    // Convert mat224 into a float array that can be sent to Tensorflow
    val rgbBytes: ByteBuffer = ByteBuffer.allocate(1 * 4 * 224 * 224 * 3)
    rgbBytes.order(ByteOrder.nativeOrder())

    val frameBitmap = Bitmap.createBitmap(imageFrame.cols(), imageFrame.rows(), Bitmap.Config.ARGB_8888, true)
    // convert Mat to Bitmap
    Utils.matToBitmap(imageFrame, frameBitmap, true)
    frameBitmap.getPixels(intValues, 0, frameBitmap.width, 0, 0, frameBitmap.width, frameBitmap.height)

    // Iterate over all pixels and retrieve information of RGB channels
    intValues.forEach { packedPixel ->
        rgbBytes.putFloat((((packedPixel shr 16) and 0xFF) - 128) / 128.0f)
        rgbBytes.putFloat((((packedPixel shr 8) and 0xFF) - 128) / 128.0f)
        rgbBytes.putFloat(((packedPixel and 0xFF) - 128) / 128.0f)
    }
}

.......
private var labelProb: Array<FloatArray>? = null
.......
// and classify 
labelProb?.let { interpreter?.run(rgbBytes, it) }
.......

Я проверил растровое изображение, которое конвертируется из Mat.Это проявляется как можно лучше.

Есть идеи у кого-нибудь?

Обновление 1

Я немного изменил реализацию метода setImageFrame, чтобы соответствовать реализации здесь .Так как это работает для него, я надеялся, что это будет работать и для меня.По-прежнему этого не происходит.

override fun setImageFrame(imageFrame: Mat) {
    // Reset the rgb bytes buffer
    rgbBytes.rewind()

    // Iterate over all pixels and retrieve information of RGB channels only
    for(rows in 0 until imageFrame.rows())
        for(cols in 0 until imageFrame.cols()) {
            val imageData = imageFrame.get(rows, cols)
            // Type of Mat is 24
            // Channels is 4
            // Depth is 0
            rgbBytes.putFloat(imageData[0].toFloat())
            rgbBytes.putFloat(imageData[1].toFloat())
            rgbBytes.putFloat(imageData[2].toFloat())
        }
}

Обновление два

Подозревая мою модель с плавающей точкой, я изменил ее на готовую модель MobileNet Quant просто для того, чтобы исключить такую ​​возможность.Проблема сохраняется и в этом.

...
Recognitions : [candle: 18.0, otterhound: 15.0, syringe: 13.0, English foxhound: 11.0]
Recognitions : [candle: 18.0, otterhound: 15.0, syringe: 13.0, English foxhound: 11.0]
Recognitions : [candle: 18.0, otterhound: 15.0, syringe: 13.0, English foxhound: 11.0]
Recognitions : [candle: 18.0, otterhound: 15.0, syringe: 13.0, English foxhound: 11.0]
...

1 Ответ

0 голосов
/ 05 октября 2018

Хорошо.так что через 4 дня я смог наконец решить это.Проблема заключалась в том, как ByteBuffer инициируется.Я делал:

private var rgbBytes: ByteBuffer = ByteBuffer.allocate(1 * 4 * 224 * 224 * 3)

вместо того, что я должен делать:

private val rgbBytes: ByteBuffer = ByteBuffer.allocateDirect(1 * 4 * 224 * 224 * 3)

Я пытался понять, в чем разница между ByteBuffer.allocate() и ByteBuffer.allocateDirect() здесь но безрезультатно.

Я был бы рад, если бы кто-нибудь ответил на еще два вопроса:

  1. Почему Tensorflow нужен прямой байтовый буфер, а не прямой буфер?
  2. Чточем разница между прямым и не прямым байтовым буфером в упрощенном описании?
...