Costuras panorámicas con código fuente de la cámara Android

Estoy tratando de crear una imagen panorámica uniendo un conjunto de mapas de bits. Para esta tarea, estoy usando el código fuente de la cámara stock de Android, que tiene un conjunto de clases Panorama (mosaico). Este código se puede encontrar en muchos repositorios, pero en cualquier caso aquí está el oficial:

https://android.googlesource.com/platform/packages/apps/Camera2/+/android-4.4.3_r1

Para los usuarios que están familiarizados con la cámara de Android original, la opción panorámica funciona al desplazar / mover la cámara que captura automáticamente los cuadros para su posterior procesamiento posterior. En mi caso, necesito coser mapas de bits sin marcos de cámara.

Así que he creado una función para aceptar mapas de bits basados en el método de la cámara de valores:

Java_com_android_camera_Mosaic_setSourceImage

A continuación, mi método para empujar mapas de bits en lugar de marcos de cámara:

JNIEXPORT jfloatArray JNICALL Java_com_android_camera_Mosaic_setSourceBitmap(JNIEnv* env, jobject thiz, jobject bitmap)
{
    int ret_code = Mosaic::MOSAIC_RET_OK;

    if (frame_number_HR < MAX_FRAMES && frame_number_LR < MAX_FRAMES)
    {
        AndroidBitmapInfo bitmapInfo;
        AndroidBitmap_getInfo(env, bitmap, &bitmapInfo);

        void* bitmapPixels;
        AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels);

        ImageUtils::rgba2yvu(tImage[HR][frame_number_HR], (unsigned char*)bitmapPixels, tWidth[HR], tHeight[HR]);
        AndroidBitmap_unlockPixels(env, bitmap);

        GenerateQuarterResImagePlanar(tImage[HR][frame_number_HR], tWidth[HR], tHeight[HR], tImage[LR][frame_number_LR]);
        decodeYUV444SP(gPreviewImage[LR], tImage[LR][frame_number_LR], gPreviewImageWidth[LR], gPreviewImageHeight[LR]);
        ret_code = AddFrame(LR, frame_number_LR, gTRS);

        if (ret_code == Mosaic::MOSAIC_RET_OK || ret_code == Mosaic::MOSAIC_RET_FEW_INLIERS)
        {
            frame_number_LR++;
            frame_number_HR++;
        }
    }
    else
    {
        gTRS[1] = gTRS[2] = gTRS[3] = gTRS[5] = gTRS[6] = gTRS[7] = 0.0f;
        gTRS[0] = gTRS[4] = gTRS[8] = 1.0f;
    }

    UpdateWarpTransformation(gTRS);

    gTRS[9] = frame_number_HR;
    gTRS[10] = ret_code;

    jfloatArray bytes = env->NewFloatArray(11);

    if (bytes != 0)
    {
        env->SetFloatArrayRegion(bytes, 0, 11, (jfloat*)gTRS);
    }

    return bytes;
}

Y para generar el panorama, hago el siguiente flujo de trabajo, también basado en el código fuente de Android:

private static Bitmap loadBitmap(final String filename)
{
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Bitmap.Config.ARGB_8888;
    Bitmap bitmap = BitmapFactory.decodeFile(filename, options);
    return bitmap;
}

public int createMosaicFromBitmaps(final boolean highRes, final List<String> images)
{
    for (String file : images)
    {
        final Bitmap bitmap = MosaicFrameProcessor.loadBitmap(file);
        this._mosaicer.setSourceBitmap(bitmap);
        bitmap.recycle();
    }

    return this._mosaicer.createMosaic(true);
}

public void generatePanorama()
{
    final MosaicFrameProcessor mosaicProcessor = new MosaicFrameProcessor(1280, 720);

    final List<String> imagesList = new ArrayList<String>();
    imagesList.add("storage/extSdCard/Test2/A1.jpg");
    imagesList.add("storage/extSdCard/Test2/A2.jpg");
    imagesList.add("storage/extSdCard/Test2/A3.jpg");

    final int mosaicReturnCode = createMosaicFromBitmaps(false, imagesList);

    if (mosaicReturnCode == Mosaic.MOSAIC_RET_CANCELLED || mosaicReturnCode == Mosaic.MOSAIC_RET_ERROR)
    {
        return;
    }

    int[] imageData = mosaicProcessor.getFinalMosaic();

    if (imageData == null)
    {
        Log.e(PanoramaActivity.TAG, "getFinalMosaic() returned null.");
        return;
    }

    final int height = imageData[imageData.length - 1];
    final int width = imageData[imageData.length - 2];

    final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    System.arraycopy(imageData, 0, imageData, 0, imageData.length - 2);
    bitmap.setPixels(imageData, 0, width, 0, 0, width, height);

    final ImageView image = (ImageView)this.findViewById(com.example.customcamera.R.id.image);
    image.setImageBitmap(bitmap);
    image.invalidate();
}

El problema al que me enfrento es que por las trazas puedo ver que se aceptan los mapas de bits de prueba, pasan la validación y se cose un panorama. Pero la imagen de salida final devuelta está toda mezclada. El resultado es un marco de imagen único que contiene secciones / partes de las 3 imágenes de prueba, además de alineado y mezclado incorrectamente.

Respuestas a la pregunta(0)

Su respuesta a la pregunta