title: Panorama stitching with Android camera source code
body: <p>I'm trying to create a panorama image by stitching a set of bitmaps.
  For this task I'm using the source code from the android stock camera, which has a Panorama (mosaic) set of classes. This code can be found in many repositories, but in any case here is the official one:</p>
  
  <p><a href="https://android.googlesource.com/platform/packages/apps/Camera2/+/android-4.4.3_r1" rel="nofollow">https://android.googlesource.com/platform/packages/apps/Camera2/+/android-4.4.3_r1</a></p>
  
  <p>For users that are familiar with the stock android camera, the panorama option works by panning/moving the camera which automatically captures frames for later post-processing.
  In my case I need to actually stitch still bitmaps and no camera frames.</p>
  
  <p>So I've created a function to accept bitmaps based in the stock camera method:</p>
  
  <pre><code>Java_com_android_camera_Mosaic_setSourceImage
  </code></pre>
  
  <p>Next my method to push bitmaps instead of camera frames:</p>
  
  <pre><code>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 &lt; MAX_FRAMES &amp;&amp; frame_number_LR &lt; MAX_FRAMES)
      {
          AndroidBitmapInfo bitmapInfo;
          AndroidBitmap_getInfo(env, bitmap, &amp;bitmapInfo);
  
          void* bitmapPixels;
          AndroidBitmap_lockPixels(env, bitmap, &amp;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-&gt;NewFloatArray(11);
  
      if (bytes != 0)
      {
          env-&gt;SetFloatArrayRegion(bytes, 0, 11, (jfloat*)gTRS);
      }
  
      return bytes;
  }
  </code></pre>
  
  <p>And to generate the panorama, I do the next workflow, also based in the android source code:</p>
  
  <pre><code>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&lt;String&gt; 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&lt;String&gt; imagesList = new ArrayList&lt;String&gt;();
      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();
  }
  </code></pre>
  
  <p>The problem I'm facing is that by the traces I can see that the test bitmaps are accepted, they pass validation, and a panorama is stitched. But the returned final output image is all mixed up. The result is a single image frame containing sections/parts of the 3 test images, plus incorrectly aligned and blended.</p>