/*
* 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.
*/
///////////////////////////////////////////////////
// Mosaic.pp
// S.O. # :
// Author(s): zkira
// $Id: Mosaic.cpp,v 1.20 2011/06/24 04:22:14 mbansal Exp $
#include <stdio.h>
#include <string.h>
#include "Mosaic.h"
#include "trsMatrix.h"
#include "Log.h"
#define LOG_TAG "MOSAIC"
Mosaic::Mosaic()
{
initialized = false;
imageMosaicYVU = NULL;
frames_size = 0;
max_frames = 200;
}
Mosaic::~Mosaic()
{
for (int i = 0; i < frames_size; i++)
{
if (frames[i])
delete frames[i];
}
delete frames;
delete rframes;
for (int j = 0; j < owned_size; j++)
delete owned_frames[j];
delete owned_frames;
if (aligner != NULL)
delete aligner;
if (blender != NULL)
delete blender;
}
int Mosaic::initialize(int blendingType, int stripType, int width, int height, int nframes, bool quarter_res, float thresh_still)
{
this->blendingType = blendingType;
// TODO: Review this logic if enabling FULL or PAN mode
if (blendingType == Blend::BLEND_TYPE_FULL ||
blendingType == Blend::BLEND_TYPE_PAN)
{
stripType = Blend::STRIP_TYPE_THIN;
}
this->stripType = stripType;
this->width = width;
this->height = height;
mosaicWidth = mosaicHeight = 0;
imageMosaicYVU = NULL;
frames = new MosaicFrame *[max_frames];
rframes = new MosaicFrame *[max_frames];
if(nframes>-1)
{
for(int i=0; i<nframes; i++)
{
frames[i] = new MosaicFrame(this->width,this->height,false); // Do no allocate memory for YUV data
}
}
else
{
for(int i=0; i<max_frames; i++)
{
frames[i] = NULL;
}
}
owned_frames = new ImageType[max_frames];
owned_size = 0;
LOGV("Initialize %d %d", width, height);
LOGV("Frame width %d,%d", width, height);
LOGV("Max num frames %d", max_frames);
aligner = new Align();
aligner->initialize(width, height,quarter_res,thresh_still);
if (blendingType == Blend::BLEND_TYPE_FULL ||
blendingType == Blend::BLEND_TYPE_PAN ||
blendingType == Blend::BLEND_TYPE_CYLPAN ||
blendingType == Blend::BLEND_TYPE_HORZ) {
blender = new Blend();
blender->initialize(blendingType, stripType, width, height);
} else {
blender = NULL;
LOGE("Error: Unknown blending type %d",blendingType);
return MOSAIC_RET_ERROR;
}
initialized = true;
return MOSAIC_RET_OK;
}
int Mosaic::addFrameRGB(ImageType imageRGB)
{
ImageType imageYVU;
// Convert to YVU24 which is used by blending
imageYVU = ImageUtils::allocateImage(this->width, this->height, ImageUtils::IMAGE_TYPE_NUM_CHANNELS);
ImageUtils::rgb2yvu(imageYVU, imageRGB, width, height);
int existing_frames_size = frames_size;
int ret = addFrame(imageYVU);
if (frames_size > existing_frames_size)
owned_frames[owned_size++] = imageYVU;
else
ImageUtils::freeImage(imageYVU);
return ret;
}
int Mosaic::addFrame(ImageType imageYVU)
{
if(frames[frames_size]==NULL)
frames[frames_size] = new MosaicFrame(this->width,this->height,false);
MosaicFrame *frame = frames[frames_size];
frame->image = imageYVU;
// Add frame to aligner
int ret = MOSAIC_RET_ERROR;
if (aligner != NULL)
{
// Note aligner takes in RGB images
int align_flag = Align::ALIGN_RET_OK;
align_flag = aligner->addFrame(frame->image);
aligner->getLastTRS(frame->trs);
if (frames_size >= max_frames)
{
LOGV("WARNING: More frames than preallocated, ignoring."
"Increase maximum number of frames (-f <max_frames>) to avoid this");
return MOSAIC_RET_ERROR;
}
switch (align_flag)
{
case Align::ALIGN_RET_OK:
frames_size++;
ret = MOSAIC_RET_OK;
break;
case Align::ALIGN_RET_FEW_INLIERS:
frames_size++;
ret = MOSAIC_RET_FEW_INLIERS;
break;
case Align::ALIGN_RET_LOW_TEXTURE:
ret = MOSAIC_RET_LOW_TEXTURE;
break;
case Align::ALIGN_RET_ERROR:
ret = MOSAIC_RET_ERROR;
break;
default:
break;
}
}
return ret;
}
int Mosaic::createMosaic(float &progress, bool &cancelComputation)
{
if (frames_size <= 0)
{
// Haven't accepted any frame in aligner. No need to do blending.
progress = TIME_PERCENT_ALIGN + TIME_PERCENT_BLEND
+ TIME_PERCENT_FINAL;
return MOSAIC_RET_OK;
}
if (blendingType == Blend::BLEND_TYPE_PAN)
{
balanceRotations();
}
int ret = Blend::BLEND_RET_ERROR;
// Blend the mosaic (alignment has already been done)
if (blender != NULL)
{
ret = blender->runBlend((MosaicFrame **) frames, (MosaicFrame **) rframes,
frames_size, imageMosaicYVU,
mosaicWidth, mosaicHeight, progress, cancelComputation);
}
switch(ret)
{
case Blend::BLEND_RET_ERROR:
case Blend::BLEND_RET_ERROR_MEMORY:
ret = MOSAIC_RET_ERROR;
break;
case Blend::BLEND_RET_CANCELLED:
ret = MOSAIC_RET_CANCELLED;
break;
case Blend::BLEND_RET_OK:
ret = MOSAIC_RET_OK;
}
return ret;
}
ImageType Mosaic::getMosaic(int &width, int &height)
{
width = mosaicWidth;
height = mosaicHeight;
return imageMosaicYVU;
}
int Mosaic::balanceRotations()
{
// Normalize to the mean angle of rotation (Smiley face)
double sineAngle = 0.0;
for (int i = 0; i < frames_size; i++) sineAngle += frames[i]->trs[0][1];
sineAngle /= frames_size;
// Calculate the cosineAngle (1 - sineAngle*sineAngle) = cosineAngle*cosineAngle
double cosineAngle = sqrt(1.0 - sineAngle*sineAngle);
double m[3][3] = {
{ cosineAngle, -sineAngle, 0 },
{ sineAngle, cosineAngle, 0},
{ 0, 0, 1}};
double tmp[3][3];
for (int i = 0; i < frames_size; i++) {
memcpy(tmp, frames[i]->trs, sizeof(tmp));
mult33d(frames[i]->trs, m, tmp);
}
return MOSAIC_RET_OK;
}