diff options
Diffstat (limited to 'jni/feature_mos_jni.cpp')
-rw-r--r-- | jni/feature_mos_jni.cpp | 669 |
1 files changed, 669 insertions, 0 deletions
diff --git a/jni/feature_mos_jni.cpp b/jni/feature_mos_jni.cpp new file mode 100644 index 000000000..0fa792a6b --- /dev/null +++ b/jni/feature_mos_jni.cpp @@ -0,0 +1,669 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +* + */ +#include <string.h> +#include <jni.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <db_utilities_camera.h> + +#include "mosaic/AlignFeatures.h" +#include "mosaic/Blend.h" +#include "mosaic/Mosaic.h" +#include "mosaic/Log.h" +#define LOG_TAG "FEATURE_MOS_JNI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mosaic_renderer_jni.h" + +char buffer[1024]; + +const int MAX_FRAMES = 100; + +static double mTx; + +int tWidth[NR]; +int tHeight[NR]; + +ImageType tImage[NR][MAX_FRAMES];// = {{ImageUtils::IMAGE_TYPE_NOIMAGE}}; // YVU24 format image +Mosaic *mosaic[NR] = {NULL,NULL}; +ImageType resultYVU = ImageUtils::IMAGE_TYPE_NOIMAGE; +ImageType resultBGR = ImageUtils::IMAGE_TYPE_NOIMAGE; +float gTRS[11]; // 9 elements of the transformation, 1 for frame-number, 1 for alignment error code. +// Variables to keep track of the mosaic computation progress for both LR & HR. +float gProgress[NR]; +// Variables to be able to cancel the mosaic computation when the GUI says so. +bool gCancelComputation[NR]; + +int c; +int width=0, height=0; +int mosaicWidth=0, mosaicHeight=0; + +//int blendingType = Blend::BLEND_TYPE_FULL; +//int blendingType = Blend::BLEND_TYPE_CYLPAN; +int blendingType = Blend::BLEND_TYPE_HORZ; +int stripType = Blend::STRIP_TYPE_THIN; +bool high_res = false; +bool quarter_res[NR] = {false,false}; +float thresh_still[NR] = {5.0f,0.0f}; + +/* return current time in milliseconds*/ + +#ifndef now_ms +static double +now_ms(void) +{ + //struct timespec res; + struct timeval res; + //clock_gettime(CLOCK_REALTIME, &res); + gettimeofday(&res, NULL); + return 1000.0*res.tv_sec + (double)res.tv_usec/1e3; +} +#endif + + +static int frame_number_HR = 0; +static int frame_number_LR = 0; + +int Init(int mID, int nmax) +{ + double t0, t1, time_c; + + if(mosaic[mID]!=NULL) + { + delete mosaic[mID]; + mosaic[mID] = NULL; + } + + mosaic[mID] = new Mosaic(); + + t0 = now_ms(); + + // When processing higher than 720x480 video, process low-res at + // quarter resolution + if(tWidth[LR]>180) + quarter_res[LR] = true; + + + // Check for initialization and if not, initialize + if (!mosaic[mID]->isInitialized()) + { + mosaic[mID]->initialize(blendingType, stripType, tWidth[mID], tHeight[mID], + nmax, quarter_res[mID], thresh_still[mID]); + } + + t1 = now_ms(); + time_c = t1 - t0; + LOGV("Init[%d]: %g ms [%d frames]",mID,time_c,nmax); + return 1; +} + +void GenerateQuarterResImagePlanar(ImageType im, int input_w, int input_h, + ImageType &out) +{ + ImageType imp; + ImageType outp; + + int count = 0; + + for (int j = 0; j < input_h; j += H2L_FACTOR) + { + imp = im + j * input_w; + outp = out + (j / H2L_FACTOR) * (input_w / H2L_FACTOR); + + for (int i = 0; i < input_w; i += H2L_FACTOR) + { + *outp++ = *(imp + i); + count++; + } + } + + for (int j = input_h; j < 2 * input_h; j += H2L_FACTOR) + { + imp = im + j * input_w; + outp = out + (j / H2L_FACTOR) * (input_w / H2L_FACTOR); + + for (int i = 0; i < input_w; i += H2L_FACTOR) + { + *outp++ = *(imp + i); + count++; + } + } + + for (int j = 2 * input_h; j < 3 * input_h; j += H2L_FACTOR) + { + imp = im + j * input_w; + outp = out + (j / H2L_FACTOR) * (input_w / H2L_FACTOR); + + for (int i = 0; i < input_w; i += H2L_FACTOR) + { + *outp++ = *(imp + i); + count++; + } + } +} + +int AddFrame(int mID, int k, float* trs1d) +{ + double t0, t1, time_c; + double trs[3][3]; + + int ret_code = mosaic[mID]->addFrame(tImage[mID][k]); + + mosaic[mID]->getAligner()->getLastTRS(trs); + + if(trs1d!=NULL) + { + + trs1d[0] = trs[0][0]; + trs1d[1] = trs[0][1]; + trs1d[2] = trs[0][2]; + trs1d[3] = trs[1][0]; + trs1d[4] = trs[1][1]; + trs1d[5] = trs[1][2]; + trs1d[6] = trs[2][0]; + trs1d[7] = trs[2][1]; + trs1d[8] = trs[2][2]; + } + + return ret_code; +} + +int Finalize(int mID) +{ + double t0, t1, time_c; + + t0 = now_ms(); + // Create the mosaic + int ret = mosaic[mID]->createMosaic(gProgress[mID], gCancelComputation[mID]); + t1 = now_ms(); + time_c = t1 - t0; + LOGV("CreateMosaic: %g ms",time_c); + + // Get back the result + resultYVU = mosaic[mID]->getMosaic(mosaicWidth, mosaicHeight); + + return ret; +} + +void YUV420toYVU24(ImageType yvu24, ImageType yuv420sp, int width, int height) +{ + int frameSize = width * height; + + ImageType oyp = yvu24; + ImageType ovp = yvu24+frameSize; + ImageType oup = yvu24+frameSize+frameSize; + + for (int j = 0, yp = 0; j < height; j++) + { + unsigned char u = 0, v = 0; + int uvp = frameSize + (j >> 1) * width; + for (int i = 0; i < width; i++, yp++) + { + *oyp++ = yuv420sp[yp]; + //int y = (0xff & (int)yuv420sp[yp]) -16; + //yvu24p[yp] = (y<0)?0:y; + + if ((i & 1) == 0) + { + v = yuv420sp[uvp++]; + u = yuv420sp[uvp++]; + } + + *ovp++ = v; + *oup++ = u; + } + } +} + +void YUV420toYVU24_NEW(ImageType yvu24, ImageType yuv420sp, int width, + int height) +{ + int frameSize = width * height; + + ImageType oyp = yvu24; + ImageType ovp = yvu24 + frameSize; + ImageType oup = yvu24 + frameSize + frameSize; + + memcpy(yvu24, yuv420sp, frameSize * sizeof(unsigned char)); + + for (int j = 0; j < height; j += 2) + { + unsigned char u = 0, v = 0; + int uvp = frameSize + (j >> 1) * width; + ovp = yvu24 + frameSize + j * width; + oup = ovp + frameSize; + + ImageType iuvp = yuv420sp + uvp; + + for (int i = 0; i < width; i += 2) + { + v = *iuvp++; + u = *iuvp++; + + *ovp++ = v; + *oup++ = u; + + *ovp++ = v; + *oup++ = u; + + } + memcpy(ovp, ovp - width, width * sizeof(unsigned char)); + memcpy(oup, oup - width, width * sizeof(unsigned char)); + } +} + + +JNIEXPORT void JNICALL Java_com_android_camera_Mosaic_allocateMosaicMemory( + JNIEnv* env, jobject thiz, jint width, jint height) +{ + tWidth[HR] = width; + tHeight[HR] = height; + tWidth[LR] = int(width / H2L_FACTOR); + tHeight[LR] = int(height / H2L_FACTOR); + + for(int i=0; i<MAX_FRAMES; i++) + { + tImage[LR][i] = ImageUtils::allocateImage(tWidth[LR], tHeight[LR], + ImageUtils::IMAGE_TYPE_NUM_CHANNELS); + tImage[HR][i] = ImageUtils::allocateImage(tWidth[HR], tHeight[HR], + ImageUtils::IMAGE_TYPE_NUM_CHANNELS); + } + + AllocateTextureMemory(tWidth[HR], tHeight[HR], tWidth[LR], tHeight[LR]); +} + +JNIEXPORT void JNICALL Java_com_android_camera_Mosaic_freeMosaicMemory( + JNIEnv* env, jobject thiz) +{ + for(int i = 0; i < MAX_FRAMES; i++) + { + ImageUtils::freeImage(tImage[LR][i]); + ImageUtils::freeImage(tImage[HR][i]); + } + + FreeTextureMemory(); +} + + +void decodeYUV444SP(unsigned char* rgb, unsigned char* yuv420sp, int width, + int height) +{ + int frameSize = width * height; + + for (int j = 0, yp = 0; j < height; j++) + { + int vp = frameSize + j * width, u = 0, v = 0; + int up = vp + frameSize; + + for (int i = 0; i < width; i++, yp++, vp++, up++) + { + int y = (0xff & ((int) yuv420sp[yp])) - 16; + if (y < 0) y = 0; + + v = (0xff & yuv420sp[vp]) - 128; + u = (0xff & yuv420sp[up]) - 128; + + int y1192 = 1192 * y; + int r = (y1192 + 1634 * v); + int g = (y1192 - 833 * v - 400 * u); + int b = (y1192 + 2066 * u); + + if (r < 0) r = 0; else if (r > 262143) r = 262143; + if (g < 0) g = 0; else if (g > 262143) g = 262143; + if (b < 0) b = 0; else if (b > 262143) b = 262143; + + //rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff); + int p = j*width*3+i*3; + rgb[p+0] = (r<<6 & 0xFF0000)>>16; + rgb[p+1] = (g>>2 & 0xFF00)>>8; + rgb[p+2] = b>>10 & 0xFF; + } + } +} + +static int count = 0; + +void ConvertYVUAiToPlanarYVU(unsigned char *planar, unsigned char *in, int width, + int height) +{ + int planeSize = width * height; + unsigned char* Yptr = planar; + unsigned char* Vptr = planar + planeSize; + unsigned char* Uptr = Vptr + planeSize; + + for (int i = 0; i < planeSize; i++) + { + *Yptr++ = *in++; + *Vptr++ = *in++; + *Uptr++ = *in++; + in++; // Alpha + } +} + +JNIEXPORT jfloatArray JNICALL Java_com_android_camera_Mosaic_setSourceImageFromGPU( + JNIEnv* env, jobject thiz) +{ + double t0, t1, time_c; + t0 = now_ms(); + int ret_code = Mosaic::MOSAIC_RET_OK; + + if(frame_number_HR<MAX_FRAMES && frame_number_LR<MAX_FRAMES) + { + double last_tx = mTx; + + sem_wait(&gPreviewImage_semaphore); + ConvertYVUAiToPlanarYVU(tImage[LR][frame_number_LR], gPreviewImage[LR], + tWidth[LR], tHeight[LR]); + + sem_post(&gPreviewImage_semaphore); + + ret_code = AddFrame(LR, frame_number_LR, gTRS); + + if(ret_code == Mosaic::MOSAIC_RET_OK || ret_code == Mosaic::MOSAIC_RET_FEW_INLIERS) + { + // Copy into HR buffer only if this is a valid frame + sem_wait(&gPreviewImage_semaphore); + ConvertYVUAiToPlanarYVU(tImage[HR][frame_number_HR], gPreviewImage[HR], + tWidth[HR], tHeight[HR]); + sem_post(&gPreviewImage_semaphore); + + 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; +} + + + +JNIEXPORT jfloatArray JNICALL Java_com_android_camera_Mosaic_setSourceImage( + JNIEnv* env, jobject thiz, jbyteArray photo_data) +{ + double t0, t1, time_c; + t0 = now_ms(); + + int ret_code = Mosaic::MOSAIC_RET_OK; + + if(frame_number_HR<MAX_FRAMES && frame_number_LR<MAX_FRAMES) + { + jbyte *pixels = env->GetByteArrayElements(photo_data, 0); + + YUV420toYVU24_NEW(tImage[HR][frame_number_HR], (ImageType)pixels, + tWidth[HR], tHeight[HR]); + + env->ReleaseByteArrayElements(photo_data, pixels, 0); + + double last_tx = mTx; + + t0 = now_ms(); + GenerateQuarterResImagePlanar(tImage[HR][frame_number_HR], tWidth[HR], + tHeight[HR], tImage[LR][frame_number_LR]); + + + sem_wait(&gPreviewImage_semaphore); + decodeYUV444SP(gPreviewImage[LR], tImage[LR][frame_number_LR], + gPreviewImageWidth[LR], gPreviewImageHeight[LR]); + sem_post(&gPreviewImage_semaphore); + + 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; +} + +JNIEXPORT void JNICALL Java_com_android_camera_Mosaic_setBlendingType( + JNIEnv* env, jobject thiz, jint type) +{ + blendingType = int(type); +} + +JNIEXPORT void JNICALL Java_com_android_camera_Mosaic_setStripType( + JNIEnv* env, jobject thiz, jint type) +{ + stripType = int(type); +} + +JNIEXPORT void JNICALL Java_com_android_camera_Mosaic_reset( + JNIEnv* env, jobject thiz) +{ + frame_number_HR = 0; + frame_number_LR = 0; + + gProgress[LR] = 0.0; + gProgress[HR] = 0.0; + + gCancelComputation[LR] = false; + gCancelComputation[HR] = false; + + Init(LR,MAX_FRAMES); +} + +JNIEXPORT jint JNICALL Java_com_android_camera_Mosaic_reportProgress( + JNIEnv* env, jobject thiz, jboolean hires, jboolean cancel_computation) +{ + if(bool(hires)) + gCancelComputation[HR] = cancel_computation; + else + gCancelComputation[LR] = cancel_computation; + + if(bool(hires)) + return (jint) gProgress[HR]; + else + return (jint) gProgress[LR]; +} + +JNIEXPORT jint JNICALL Java_com_android_camera_Mosaic_createMosaic( + JNIEnv* env, jobject thiz, jboolean value) +{ + high_res = bool(value); + + int ret; + + if(high_res) + { + LOGV("createMosaic() - High-Res Mode"); + double t0, t1, time_c; + + gProgress[HR] = 0.0; + t0 = now_ms(); + + Init(HR, frame_number_HR); + + for(int k = 0; k < frame_number_HR; k++) + { + if (gCancelComputation[HR]) + break; + AddFrame(HR, k, NULL); + gProgress[HR] += TIME_PERCENT_ALIGN/frame_number_HR; + } + + if (gCancelComputation[HR]) + { + ret = Mosaic::MOSAIC_RET_CANCELLED; + } + else + { + gProgress[HR] = TIME_PERCENT_ALIGN; + + t1 = now_ms(); + time_c = t1 - t0; + LOGV("AlignAll - %d frames [HR]: %g ms", frame_number_HR, time_c); + + ret = Finalize(HR); + + gProgress[HR] = 100.0; + } + + high_res = false; + } + else + { + LOGV("createMosaic() - Low-Res Mode"); + gProgress[LR] = TIME_PERCENT_ALIGN; + + ret = Finalize(LR); + + gProgress[LR] = 100.0; + } + + return (jint) ret; +} + +JNIEXPORT jintArray JNICALL Java_com_android_camera_Mosaic_getFinalMosaic( + JNIEnv* env, jobject thiz) +{ + int y,x; + int width = mosaicWidth; + int height = mosaicHeight; + int imageSize = width * height; + + // Convert back to RGB24 + resultBGR = ImageUtils::allocateImage(mosaicWidth, mosaicHeight, + ImageUtils::IMAGE_TYPE_NUM_CHANNELS); + ImageUtils::yvu2bgr(resultBGR, resultYVU, mosaicWidth, mosaicHeight); + + LOGV("MosBytes: %d, W = %d, H = %d", imageSize, width, height); + + int* image = new int[imageSize]; + int* dims = new int[2]; + + for(y=0; y<height; y++) + { + for(x=0; x<width; x++) + { + image[y*width+x] = (0xFF<<24) | (resultBGR[y*width*3+x*3+2]<<16)| + (resultBGR[y*width*3+x*3+1]<<8)| (resultBGR[y*width*3+x*3]); + } + } + + dims[0] = width; + dims[1] = height; + + ImageUtils::freeImage(resultBGR); + + jintArray bytes = env->NewIntArray(imageSize+2); + if (bytes == 0) { + LOGE("Error in creating the image."); + delete[] image; + return 0; + } + env->SetIntArrayRegion(bytes, 0, imageSize, (jint*) image); + env->SetIntArrayRegion(bytes, imageSize, 2, (jint*) dims); + delete[] image; + delete[] dims; + return bytes; +} + +JNIEXPORT jbyteArray JNICALL Java_com_android_camera_Mosaic_getFinalMosaicNV21( + JNIEnv* env, jobject thiz) +{ + int y,x; + int width; + int height; + + width = mosaicWidth; + height = mosaicHeight; + + int imageSize = 1.5*width * height; + + // Convert YVU to NV21 format in-place + ImageType V = resultYVU+mosaicWidth*mosaicHeight; + ImageType U = V+mosaicWidth*mosaicHeight; + for(int j=0; j<mosaicHeight/2; j++) + { + for(int i=0; i<mosaicWidth; i+=2) + { + V[j*mosaicWidth+i] = V[(2*j)*mosaicWidth+i]; // V + V[j*mosaicWidth+i+1] = U[(2*j)*mosaicWidth+i]; // U + } + } + + LOGV("MosBytes: %d, W = %d, H = %d", imageSize, width, height); + + unsigned char* dims = new unsigned char[8]; + + dims[0] = (unsigned char)(width >> 24); + dims[1] = (unsigned char)(width >> 16); + dims[2] = (unsigned char)(width >> 8); + dims[3] = (unsigned char)width; + + dims[4] = (unsigned char)(height >> 24); + dims[5] = (unsigned char)(height >> 16); + dims[6] = (unsigned char)(height >> 8); + dims[7] = (unsigned char)height; + + jbyteArray bytes = env->NewByteArray(imageSize+8); + if (bytes == 0) { + LOGE("Error in creating the image."); + ImageUtils::freeImage(resultYVU); + return 0; + } + env->SetByteArrayRegion(bytes, 0, imageSize, (jbyte*) resultYVU); + env->SetByteArrayRegion(bytes, imageSize, 8, (jbyte*) dims); + delete[] dims; + ImageUtils::freeImage(resultYVU); + return bytes; +} + +#ifdef __cplusplus +} +#endif |