/*
* 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.
*/
///////////////////////////////////////////////////
// AlignFeatures.cpp
// S.O. # :
// Author(s): zkira, mbansal, bsouthall, narodits
// $Id: AlignFeatures.cpp,v 1.20 2011/06/17 13:35:47 mbansal Exp $
#include <stdio.h>
#include <string.h>
#include "trsMatrix.h"
#include "MatrixUtils.h"
#include "AlignFeatures.h"
#include "Log.h"
#define LOG_TAG "AlignFeatures"
Align::Align()
{
width = height = 0;
frame_number = 0;
num_frames_captured = 0;
reference_frame_index = 0;
db_Identity3x3(Hcurr);
db_Identity3x3(Hprev);
}
Align::~Align()
{
// Free gray-scale image
if (imageGray != ImageUtils::IMAGE_TYPE_NOIMAGE)
ImageUtils::freeImage(imageGray);
}
char* Align::getRegProfileString()
{
return reg.profile_string;
}
int Align::initialize(int width, int height, bool _quarter_res, float _thresh_still)
{
int nr_corners = DEFAULT_NR_CORNERS;
double max_disparity = DEFAULT_MAX_DISPARITY;
int motion_model_type = DEFAULT_MOTION_MODEL;
int nrsamples = DB_DEFAULT_NR_SAMPLES;
double scale = DB_POINT_STANDARDDEV;
int chunk_size = DB_DEFAULT_CHUNK_SIZE;
int nrhorz = width/48; // Empirically determined number of horizontal
int nrvert = height/60; // and vertical buckets for harris corner detection.
bool linear_polish = false;
unsigned int reference_update_period = DEFAULT_REFERENCE_UPDATE_PERIOD;
const bool DEFAULT_USE_SMALLER_MATCHING_WINDOW = false;
bool use_smaller_matching_window = DEFAULT_USE_SMALLER_MATCHING_WINDOW;
quarter_res = _quarter_res;
thresh_still = _thresh_still;
frame_number = 0;
num_frames_captured = 0;
reference_frame_index = 0;
db_Identity3x3(Hcurr);
db_Identity3x3(Hprev);
if (!reg.Initialized())
{
reg.Init(width, height, motion_model_type, 20, linear_polish, quarter_res,
scale, reference_update_period, false, 0, nrsamples, chunk_size,
nr_corners, max_disparity, use_smaller_matching_window,
nrhorz, nrvert);
}
this->width = width;
this->height = height;
imageGray = ImageUtils::allocateImage(width, height, 1);
if (reg.Initialized())
return ALIGN_RET_OK;
else
return ALIGN_RET_ERROR;
}
int Align::addFrameRGB(ImageType imageRGB)
{
ImageUtils::rgb2gray(imageGray, imageRGB, width, height);
return addFrame(imageGray);
}
int Align::addFrame(ImageType imageGray_)
{
int ret_code = ALIGN_RET_OK;
// Obtain a vector of pointers to rows in image and pass in to dbreg
ImageType *m_rows = ImageUtils::imageTypeToRowPointers(imageGray_, width, height);
if (frame_number == 0)
{
reg.AddFrame(m_rows, Hcurr, true); // Force this to be a reference frame
int num_corner_ref = reg.GetNrRefCorners();
if (num_corner_ref < MIN_NR_REF_CORNERS)
{
return ALIGN_RET_LOW_TEXTURE;
}
}
else
{
reg.AddFrame(m_rows, Hcurr, false);
}
// Average translation per frame =
// [Translation from Frame0 to Frame(n-1)] / [(n-1)]
average_tx_per_frame = (num_frames_captured < 2) ? 0.0 :
Hprev[2] / (num_frames_captured - 1);
// Increment the captured frame counter if we already have a reference frame
num_frames_captured++;
if (frame_number != 0)
{
int num_inliers = reg.GetNrInliers();
if(num_inliers < MIN_NR_INLIERS)
{
ret_code = ALIGN_RET_FEW_INLIERS;
Hcurr[0] = 1.0;
Hcurr[1] = 0.0;
// Set this as the average per frame translation taking into acccount
// the separation of the current frame from the reference frame...
Hcurr[2] = -average_tx_per_frame *
(num_frames_captured - reference_frame_index);
Hcurr[3] = 0.0;
Hcurr[4] = 1.0;
Hcurr[5] = 0.0;
Hcurr[6] = 0.0;
Hcurr[7] = 0.0;
Hcurr[8] = 1.0;
}
if(fabs(Hcurr[2])<thresh_still && fabs(Hcurr[5])<thresh_still) // Still camera
{
return ALIGN_RET_ERROR;
}
// compute the homography:
double Hinv33[3][3];
double Hprev33[3][3];
double Hcurr33[3][3];
// Invert and multiple with previous transformation
Matrix33::convert9to33(Hcurr33, Hcurr);
Matrix33::convert9to33(Hprev33, Hprev);
normProjMat33d(Hcurr33);
inv33d(Hcurr33, Hinv33);
mult33d(Hcurr33, Hprev33, Hinv33);
normProjMat33d(Hcurr33);
Matrix9::convert33to9(Hprev, Hcurr33);
// Since we have already factored the current transformation
// into Hprev, we can reset the Hcurr to identity
db_Identity3x3(Hcurr);
// Update the reference frame to be the current frame
reg.UpdateReference(m_rows,quarter_res,false);
// Update the reference frame index
reference_frame_index = num_frames_captured;
}
frame_number++;
return ret_code;
}
// Get current transformation
int Align::getLastTRS(double trs[3][3])
{
if (frame_number < 1)
{
trs[0][0] = 1.0;
trs[0][1] = 0.0;
trs[0][2] = 0.0;
trs[1][0] = 0.0;
trs[1][1] = 1.0;
trs[1][2] = 0.0;
trs[2][0] = 0.0;
trs[2][1] = 0.0;
trs[2][2] = 1.0;
return ALIGN_RET_ERROR;
}
// Note that the logic here handles the case, where a frame is not used for
// mosaicing but is captured and used in the preview-rendering.
// For these frames, we don't set Hcurr to identity in AddFrame() and the
// logic here appends their transformation to Hprev to render them with the
// correct transformation. For the frames we do use for mosaicing, we already
// append their Hcurr to Hprev in AddFrame() and then set Hcurr to identity.
double Hinv33[3][3];
double Hprev33[3][3];
double Hcurr33[3][3];
Matrix33::convert9to33(Hcurr33, Hcurr);
normProjMat33d(Hcurr33);
inv33d(Hcurr33, Hinv33);
Matrix33::convert9to33(Hprev33, Hprev);
mult33d(trs, Hprev33, Hinv33);
normProjMat33d(trs);
return ALIGN_RET_OK;
}