diff options
Diffstat (limited to 'jni_mosaic/feature_mos/src/mosaic/Blend.cpp')
-rw-r--r-- | jni_mosaic/feature_mos/src/mosaic/Blend.cpp | 1410 |
1 files changed, 0 insertions, 1410 deletions
diff --git a/jni_mosaic/feature_mos/src/mosaic/Blend.cpp b/jni_mosaic/feature_mos/src/mosaic/Blend.cpp deleted file mode 100644 index ef983ff67..000000000 --- a/jni_mosaic/feature_mos/src/mosaic/Blend.cpp +++ /dev/null @@ -1,1410 +0,0 @@ -/* - * 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. - */ - -/////////////////////////////////////////////////// -// Blend.cpp -// $Id: Blend.cpp,v 1.22 2011/06/24 04:22:14 mbansal Exp $ - -#include <string.h> - -#include "Interp.h" -#include "Blend.h" - -#include "Geometry.h" -#include "trsMatrix.h" - -#include "Log.h" -#define LOG_TAG "BLEND" - -Blend::Blend() -{ - m_wb.blendingType = BLEND_TYPE_NONE; -} - -Blend::~Blend() -{ - if (m_pFrameVPyr) free(m_pFrameVPyr); - if (m_pFrameUPyr) free(m_pFrameUPyr); - if (m_pFrameYPyr) free(m_pFrameYPyr); -} - -int Blend::initialize(int blendingType, int stripType, int frame_width, int frame_height) -{ - this->width = frame_width; - this->height = frame_height; - this->m_wb.blendingType = blendingType; - this->m_wb.stripType = stripType; - - m_wb.blendRange = m_wb.blendRangeUV = BLEND_RANGE_DEFAULT; - m_wb.nlevs = m_wb.blendRange; - m_wb.nlevsC = m_wb.blendRangeUV; - - if (m_wb.nlevs <= 0) m_wb.nlevs = 1; // Need levels for YUV processing - if (m_wb.nlevsC > m_wb.nlevs) m_wb.nlevsC = m_wb.nlevs; - - m_wb.roundoffOverlap = 1.5; - - m_pFrameYPyr = NULL; - m_pFrameUPyr = NULL; - m_pFrameVPyr = NULL; - - m_pFrameYPyr = PyramidShort::allocatePyramidPacked(m_wb.nlevs, (unsigned short) width, (unsigned short) height, BORDER); - m_pFrameUPyr = PyramidShort::allocatePyramidPacked(m_wb.nlevsC, (unsigned short) (width), (unsigned short) (height), BORDER); - m_pFrameVPyr = PyramidShort::allocatePyramidPacked(m_wb.nlevsC, (unsigned short) (width), (unsigned short) (height), BORDER); - - if (!m_pFrameYPyr || !m_pFrameUPyr || !m_pFrameVPyr) - { - LOGE("Error: Could not allocate pyramids for blending"); - return BLEND_RET_ERROR_MEMORY; - } - - return BLEND_RET_OK; -} - -inline double max(double a, double b) { return a > b ? a : b; } -inline double min(double a, double b) { return a < b ? a : b; } - -void Blend::AlignToMiddleFrame(MosaicFrame **frames, int frames_size) -{ - // Unwarp this frame and Warp the others to match - MosaicFrame *mb = NULL; - MosaicFrame *ref = frames[int(frames_size/2)]; // Middle frame - - double invtrs[3][3]; - inv33d(ref->trs, invtrs); - - for(int mfit = 0; mfit < frames_size; mfit++) - { - mb = frames[mfit]; - double temp[3][3]; - mult33d(temp, invtrs, mb->trs); - memcpy(mb->trs, temp, sizeof(temp)); - normProjMat33d(mb->trs); - } -} - -int Blend::runBlend(MosaicFrame **oframes, MosaicFrame **rframes, - int frames_size, - ImageType &imageMosaicYVU, int &mosaicWidth, int &mosaicHeight, - float &progress, bool &cancelComputation) -{ - int ret; - int numCenters; - - MosaicFrame **frames; - - // For THIN strip mode, accept all frames for blending - if (m_wb.stripType == STRIP_TYPE_THIN) - { - frames = oframes; - } - else // For WIDE strip mode, first select the relevant frames to blend. - { - SelectRelevantFrames(oframes, frames_size, rframes, frames_size); - frames = rframes; - } - - ComputeBlendParameters(frames, frames_size, true); - numCenters = frames_size; - - if (numCenters == 0) - { - LOGE("Error: No frames to blend"); - return BLEND_RET_ERROR; - } - - if (!(m_AllSites = m_Triangulator.allocMemory(numCenters))) - { - return BLEND_RET_ERROR_MEMORY; - } - - // Bounding rectangle (real numbers) of the final mosaic computed by projecting - // each input frame into the mosaic coordinate system. - BlendRect global_rect; - - global_rect.lft = global_rect.bot = 2e30; // min values - global_rect.rgt = global_rect.top = -2e30; // max values - MosaicFrame *mb = NULL; - double halfwidth = width / 2.0; - double halfheight = height / 2.0; - - double z, x0, y0, x1, y1, x2, y2, x3, y3; - - // Corners of the left-most and right-most frames respectively in the - // mosaic coordinate system. - double xLeftCorners[2] = {2e30, 2e30}; - double xRightCorners[2] = {-2e30, -2e30}; - - // Corners of the top-most and bottom-most frames respectively in the - // mosaic coordinate system. - double yTopCorners[2] = {2e30, 2e30}; - double yBottomCorners[2] = {-2e30, -2e30}; - - - // Determine the extents of the final mosaic - CSite *csite = m_AllSites ; - for(int mfit = 0; mfit < frames_size; mfit++) - { - mb = frames[mfit]; - - // Compute clipping for this frame's rect - FrameToMosaicRect(mb->width, mb->height, mb->trs, mb->brect); - // Clip global rect using this frame's rect - ClipRect(mb->brect, global_rect); - - // Calculate the corner points - FrameToMosaic(mb->trs, 0.0, 0.0, x0, y0); - FrameToMosaic(mb->trs, 0.0, mb->height-1.0, x1, y1); - FrameToMosaic(mb->trs, mb->width-1.0, mb->height-1.0, x2, y2); - FrameToMosaic(mb->trs, mb->width-1.0, 0.0, x3, y3); - - if(x0 < xLeftCorners[0] || x1 < xLeftCorners[1]) // If either of the left corners is lower - { - xLeftCorners[0] = x0; - xLeftCorners[1] = x1; - } - - if(x3 > xRightCorners[0] || x2 > xRightCorners[1]) // If either of the right corners is higher - { - xRightCorners[0] = x3; - xRightCorners[1] = x2; - } - - if(y0 < yTopCorners[0] || y3 < yTopCorners[1]) // If either of the top corners is lower - { - yTopCorners[0] = y0; - yTopCorners[1] = y3; - } - - if(y1 > yBottomCorners[0] || y2 > yBottomCorners[1]) // If either of the bottom corners is higher - { - yBottomCorners[0] = y1; - yBottomCorners[1] = y2; - } - - - // Compute the centroid of the warped region - FindQuadCentroid(x0, y0, x1, y1, x2, y2, x3, y3, csite->getVCenter().x, csite->getVCenter().y); - - csite->setMb(mb); - csite++; - } - - // Get origin and sizes - - // Bounding rectangle (int numbers) of the final mosaic computed by projecting - // each input frame into the mosaic coordinate system. - MosaicRect fullRect; - - fullRect.left = (int) floor(global_rect.lft); // min-x - fullRect.top = (int) floor(global_rect.bot); // min-y - fullRect.right = (int) ceil(global_rect.rgt); // max-x - fullRect.bottom = (int) ceil(global_rect.top);// max-y - Mwidth = (unsigned short) (fullRect.right - fullRect.left + 1); - Mheight = (unsigned short) (fullRect.bottom - fullRect.top + 1); - - int xLeftMost, xRightMost; - int yTopMost, yBottomMost; - - // Rounding up, so that we don't include the gray border. - xLeftMost = max(0, max(xLeftCorners[0], xLeftCorners[1]) - fullRect.left + 1); - xRightMost = min(Mwidth - 1, min(xRightCorners[0], xRightCorners[1]) - fullRect.left - 1); - - yTopMost = max(0, max(yTopCorners[0], yTopCorners[1]) - fullRect.top + 1); - yBottomMost = min(Mheight - 1, min(yBottomCorners[0], yBottomCorners[1]) - fullRect.top - 1); - - if (xRightMost <= xLeftMost || yBottomMost <= yTopMost) - { - LOGE("RunBlend: aborting -consistency check failed," - "(xLeftMost, xRightMost, yTopMost, yBottomMost): (%d, %d, %d, %d)", - xLeftMost, xRightMost, yTopMost, yBottomMost); - return BLEND_RET_ERROR; - } - - // Make sure image width is multiple of 4 - Mwidth = (unsigned short) ((Mwidth + 3) & ~3); - Mheight = (unsigned short) ((Mheight + 3) & ~3); // Round up. - - ret = MosaicSizeCheck(LIMIT_SIZE_MULTIPLIER, LIMIT_HEIGHT_MULTIPLIER); - if (ret != BLEND_RET_OK) - { - LOGE("RunBlend: aborting - mosaic size check failed, " - "(frame_width, frame_height) vs (mosaic_width, mosaic_height): " - "(%d, %d) vs (%d, %d)", width, height, Mwidth, Mheight); - return ret; - } - - LOGI("Allocate mosaic image for blending - size: %d x %d", Mwidth, Mheight); - YUVinfo *imgMos = YUVinfo::allocateImage(Mwidth, Mheight); - if (imgMos == NULL) - { - LOGE("RunBlend: aborting - couldn't alloc %d x %d mosaic image", Mwidth, Mheight); - return BLEND_RET_ERROR_MEMORY; - } - - // Set the Y image to 255 so we can distinguish when frame idx are written to it - memset(imgMos->Y.ptr[0], 255, (imgMos->Y.width * imgMos->Y.height)); - // Set the v and u images to black - memset(imgMos->V.ptr[0], 128, (imgMos->V.width * imgMos->V.height) << 1); - - // Do the triangulation. It returns a sorted list of edges - SEdgeVector *edge; - int n = m_Triangulator.triangulate(&edge, numCenters, width, height); - m_Triangulator.linkNeighbors(edge, n, numCenters); - - // Bounding rectangle that determines the positioning of the rectangle that is - // cropped out of the computed mosaic to get rid of the gray borders. - MosaicRect cropping_rect; - - if (m_wb.horizontal) - { - cropping_rect.left = xLeftMost; - cropping_rect.right = xRightMost; - } - else - { - cropping_rect.top = yTopMost; - cropping_rect.bottom = yBottomMost; - } - - // Do merging and blending : - ret = DoMergeAndBlend(frames, numCenters, width, height, *imgMos, fullRect, - cropping_rect, progress, cancelComputation); - - if (m_wb.blendingType == BLEND_TYPE_HORZ) - CropFinalMosaic(*imgMos, cropping_rect); - - - m_Triangulator.freeMemory(); // note: can be called even if delaunay_alloc() wasn't successful - - imageMosaicYVU = imgMos->Y.ptr[0]; - - - if (m_wb.blendingType == BLEND_TYPE_HORZ) - { - mosaicWidth = cropping_rect.right - cropping_rect.left + 1; - mosaicHeight = cropping_rect.bottom - cropping_rect.top + 1; - } - else - { - mosaicWidth = Mwidth; - mosaicHeight = Mheight; - } - - return ret; -} - -int Blend::MosaicSizeCheck(float sizeMultiplier, float heightMultiplier) { - if (Mwidth < width || Mheight < height) { - return BLEND_RET_ERROR; - } - - if ((Mwidth * Mheight) > (width * height * sizeMultiplier)) { - return BLEND_RET_ERROR; - } - - // We won't do blending for the cases where users swing the device too much - // in the secondary direction. We use a short side to determine the - // secondary direction because users may hold the device in landsape - // or portrait. - int shortSide = min(Mwidth, Mheight); - if (shortSide > height * heightMultiplier) { - return BLEND_RET_ERROR; - } - - return BLEND_RET_OK; -} - -int Blend::FillFramePyramid(MosaicFrame *mb) -{ - ImageType mbY, mbU, mbV; - // Lay this image, centered into the temporary buffer - mbY = mb->image; - mbU = mb->getU(); - mbV = mb->getV(); - - int h, w; - - for(h=0; h<height; h++) - { - ImageTypeShort yptr = m_pFrameYPyr->ptr[h]; - ImageTypeShort uptr = m_pFrameUPyr->ptr[h]; - ImageTypeShort vptr = m_pFrameVPyr->ptr[h]; - - for(w=0; w<width; w++) - { - yptr[w] = (short) ((*(mbY++)) << 3); - uptr[w] = (short) ((*(mbU++)) << 3); - vptr[w] = (short) ((*(mbV++)) << 3); - } - } - - // Spread the image through the border - PyramidShort::BorderSpread(m_pFrameYPyr, BORDER, BORDER, BORDER, BORDER); - PyramidShort::BorderSpread(m_pFrameUPyr, BORDER, BORDER, BORDER, BORDER); - PyramidShort::BorderSpread(m_pFrameVPyr, BORDER, BORDER, BORDER, BORDER); - - // Generate Laplacian pyramids - if (!PyramidShort::BorderReduce(m_pFrameYPyr, m_wb.nlevs) || !PyramidShort::BorderExpand(m_pFrameYPyr, m_wb.nlevs, -1) || - !PyramidShort::BorderReduce(m_pFrameUPyr, m_wb.nlevsC) || !PyramidShort::BorderExpand(m_pFrameUPyr, m_wb.nlevsC, -1) || - !PyramidShort::BorderReduce(m_pFrameVPyr, m_wb.nlevsC) || !PyramidShort::BorderExpand(m_pFrameVPyr, m_wb.nlevsC, -1)) - { - LOGE("Error: Could not generate Laplacian pyramids"); - return BLEND_RET_ERROR; - } - else - { - return BLEND_RET_OK; - } -} - -int Blend::DoMergeAndBlend(MosaicFrame **frames, int nsite, - int width, int height, YUVinfo &imgMos, MosaicRect &rect, - MosaicRect &cropping_rect, float &progress, bool &cancelComputation) -{ - m_pMosaicYPyr = NULL; - m_pMosaicUPyr = NULL; - m_pMosaicVPyr = NULL; - - m_pMosaicYPyr = PyramidShort::allocatePyramidPacked(m_wb.nlevs,(unsigned short)rect.Width(),(unsigned short)rect.Height(),BORDER); - m_pMosaicUPyr = PyramidShort::allocatePyramidPacked(m_wb.nlevsC,(unsigned short)rect.Width(),(unsigned short)rect.Height(),BORDER); - m_pMosaicVPyr = PyramidShort::allocatePyramidPacked(m_wb.nlevsC,(unsigned short)rect.Width(),(unsigned short)rect.Height(),BORDER); - if (!m_pMosaicYPyr || !m_pMosaicUPyr || !m_pMosaicVPyr) - { - LOGE("Error: Could not allocate pyramids for blending"); - return BLEND_RET_ERROR_MEMORY; - } - - MosaicFrame *mb; - - CSite *esite = m_AllSites + nsite; - int site_idx; - - // First go through each frame and for each mosaic pixel determine which frame it should come from - site_idx = 0; - for(CSite *csite = m_AllSites; csite < esite; csite++) - { - if(cancelComputation) - { - if (m_pMosaicVPyr) free(m_pMosaicVPyr); - if (m_pMosaicUPyr) free(m_pMosaicUPyr); - if (m_pMosaicYPyr) free(m_pMosaicYPyr); - return BLEND_RET_CANCELLED; - } - - mb = csite->getMb(); - - mb->vcrect = mb->brect; - ClipBlendRect(csite, mb->vcrect); - - ComputeMask(csite, mb->vcrect, mb->brect, rect, imgMos, site_idx); - - site_idx++; - } - - ////////// imgMos.Y, imgMos.V, imgMos.U are used as follows ////////////// - ////////////////////// THIN STRIP MODE /////////////////////////////////// - - // imgMos.Y is used to store the index of the image from which each pixel - // in the output mosaic can be read out for the thin-strip mode. Thus, - // there is no special handling for pixels around the seam. Also, imgMos.Y - // is set to 255 wherever we can't get its value from any input image e.g. - // in the gray border areas. imgMos.V and imgMos.U are set to 128 for the - // thin-strip mode. - - ////////////////////// WIDE STRIP MODE /////////////////////////////////// - - // imgMos.Y is used the same way as the thin-strip mode. - // imgMos.V is used to store the index of the neighboring image which - // should contribute to the color of an output pixel in a band around - // the seam. Thus, in this band, we will crossfade between the color values - // from the image index imgMos.Y and image index imgMos.V. imgMos.U is - // used to store the weight (multiplied by 100) that each image will - // contribute to the blending process. Thus, we start at 99% contribution - // from the first image, then go to 50% contribution from each image at - // the seam. Then, the contribution from the second image goes up to 99%. - - // For WIDE mode, set the pixel masks to guide the blender to cross-fade - // between the images on either side of each seam: - if (m_wb.stripType == STRIP_TYPE_WIDE) - { - if(m_wb.horizontal) - { - // Set the number of pixels around the seam to cross-fade between - // the two component images, - int tw = STRIP_CROSS_FADE_WIDTH_PXLS; - - // Proceed with the image index calculation for cross-fading - // only if the cross-fading width is larger than 0 - if (tw > 0) - { - for(int y = 0; y < imgMos.Y.height; y++) - { - // Since we compare two adjecant pixels to determine - // whether there is a seam, the termination condition of x - // is set to imgMos.Y.width - tw, so that x+1 below - // won't exceed the imgMos' boundary. - for(int x = tw; x < imgMos.Y.width - tw; ) - { - // Determine where the seam is... - if (imgMos.Y.ptr[y][x] != imgMos.Y.ptr[y][x+1] && - imgMos.Y.ptr[y][x] != 255 && - imgMos.Y.ptr[y][x+1] != 255) - { - // Find the image indices on both sides of the seam - unsigned char idx1 = imgMos.Y.ptr[y][x]; - unsigned char idx2 = imgMos.Y.ptr[y][x+1]; - - for (int o = tw; o >= 0; o--) - { - // Set the image index to use for cross-fading - imgMos.V.ptr[y][x - o] = idx2; - // Set the intensity weights to use for cross-fading - imgMos.U.ptr[y][x - o] = 50 + (99 - 50) * o / tw; - } - - for (int o = 1; o <= tw; o++) - { - // Set the image index to use for cross-fading - imgMos.V.ptr[y][x + o] = idx1; - // Set the intensity weights to use for cross-fading - imgMos.U.ptr[y][x + o] = imgMos.U.ptr[y][x - o]; - } - - x += (tw + 1); - } - else - { - x++; - } - } - } - } - } - else - { - // Set the number of pixels around the seam to cross-fade between - // the two component images, - int tw = STRIP_CROSS_FADE_WIDTH_PXLS; - - // Proceed with the image index calculation for cross-fading - // only if the cross-fading width is larger than 0 - if (tw > 0) - { - for(int x = 0; x < imgMos.Y.width; x++) - { - // Since we compare two adjecant pixels to determine - // whether there is a seam, the termination condition of y - // is set to imgMos.Y.height - tw, so that y+1 below - // won't exceed the imgMos' boundary. - for(int y = tw; y < imgMos.Y.height - tw; ) - { - // Determine where the seam is... - if (imgMos.Y.ptr[y][x] != imgMos.Y.ptr[y+1][x] && - imgMos.Y.ptr[y][x] != 255 && - imgMos.Y.ptr[y+1][x] != 255) - { - // Find the image indices on both sides of the seam - unsigned char idx1 = imgMos.Y.ptr[y][x]; - unsigned char idx2 = imgMos.Y.ptr[y+1][x]; - - for (int o = tw; o >= 0; o--) - { - // Set the image index to use for cross-fading - imgMos.V.ptr[y - o][x] = idx2; - // Set the intensity weights to use for cross-fading - imgMos.U.ptr[y - o][x] = 50 + (99 - 50) * o / tw; - } - - for (int o = 1; o <= tw; o++) - { - // Set the image index to use for cross-fading - imgMos.V.ptr[y + o][x] = idx1; - // Set the intensity weights to use for cross-fading - imgMos.U.ptr[y + o][x] = imgMos.U.ptr[y - o][x]; - } - - y += (tw + 1); - } - else - { - y++; - } - } - } - } - } - - } - - // Now perform the actual blending using the frame assignment determined above - site_idx = 0; - for(CSite *csite = m_AllSites; csite < esite; csite++) - { - if(cancelComputation) - { - if (m_pMosaicVPyr) free(m_pMosaicVPyr); - if (m_pMosaicUPyr) free(m_pMosaicUPyr); - if (m_pMosaicYPyr) free(m_pMosaicYPyr); - return BLEND_RET_CANCELLED; - } - - mb = csite->getMb(); - - - if(FillFramePyramid(mb)!=BLEND_RET_OK) - return BLEND_RET_ERROR; - - ProcessPyramidForThisFrame(csite, mb->vcrect, mb->brect, rect, imgMos, mb->trs, site_idx); - - progress += TIME_PERCENT_BLEND/nsite; - - site_idx++; - } - - - // Blend - PerformFinalBlending(imgMos, cropping_rect); - - if (cropping_rect.Width() <= 0 || cropping_rect.Height() <= 0) - { - LOGE("Size of the cropping_rect is invalid - (width, height): (%d, %d)", - cropping_rect.Width(), cropping_rect.Height()); - return BLEND_RET_ERROR; - } - - if (m_pMosaicVPyr) free(m_pMosaicVPyr); - if (m_pMosaicUPyr) free(m_pMosaicUPyr); - if (m_pMosaicYPyr) free(m_pMosaicYPyr); - - progress += TIME_PERCENT_FINAL; - - return BLEND_RET_OK; -} - -void Blend::CropFinalMosaic(YUVinfo &imgMos, MosaicRect &cropping_rect) -{ - int i, j, k; - ImageType yimg; - ImageType uimg; - ImageType vimg; - - - yimg = imgMos.Y.ptr[0]; - uimg = imgMos.U.ptr[0]; - vimg = imgMos.V.ptr[0]; - - k = 0; - for (j = cropping_rect.top; j <= cropping_rect.bottom; j++) - { - for (i = cropping_rect.left; i <= cropping_rect.right; i++) - { - yimg[k] = yimg[j*imgMos.Y.width+i]; - k++; - } - } - for (j = cropping_rect.top; j <= cropping_rect.bottom; j++) - { - for (i = cropping_rect.left; i <= cropping_rect.right; i++) - { - yimg[k] = vimg[j*imgMos.Y.width+i]; - k++; - } - } - for (j = cropping_rect.top; j <= cropping_rect.bottom; j++) - { - for (i = cropping_rect.left; i <= cropping_rect.right; i++) - { - yimg[k] = uimg[j*imgMos.Y.width+i]; - k++; - } - } -} - -int Blend::PerformFinalBlending(YUVinfo &imgMos, MosaicRect &cropping_rect) -{ - if (!PyramidShort::BorderExpand(m_pMosaicYPyr, m_wb.nlevs, 1) || !PyramidShort::BorderExpand(m_pMosaicUPyr, m_wb.nlevsC, 1) || - !PyramidShort::BorderExpand(m_pMosaicVPyr, m_wb.nlevsC, 1)) - { - LOGE("Error: Could not BorderExpand!"); - return BLEND_RET_ERROR; - } - - ImageTypeShort myimg; - ImageTypeShort muimg; - ImageTypeShort mvimg; - ImageType yimg; - ImageType uimg; - ImageType vimg; - - int cx = (int)imgMos.Y.width/2; - int cy = (int)imgMos.Y.height/2; - - // 2D boolean array that contains true wherever the mosaic image data is - // invalid (i.e. in the gray border). - bool **b = new bool*[imgMos.Y.height]; - - for(int j=0; j<imgMos.Y.height; j++) - { - b[j] = new bool[imgMos.Y.width]; - } - - // Copy the resulting image into the full image using the mask - int i, j; - - yimg = imgMos.Y.ptr[0]; - uimg = imgMos.U.ptr[0]; - vimg = imgMos.V.ptr[0]; - - for (j = 0; j < imgMos.Y.height; j++) - { - myimg = m_pMosaicYPyr->ptr[j]; - muimg = m_pMosaicUPyr->ptr[j]; - mvimg = m_pMosaicVPyr->ptr[j]; - - for (i = 0; i<imgMos.Y.width; i++) - { - // A final mask was set up previously, - // if the value is zero skip it, otherwise replace it. - if (*yimg <255) - { - short value = (short) ((*myimg) >> 3); - if (value < 0) value = 0; - else if (value > 255) value = 255; - *yimg = (unsigned char) value; - - value = (short) ((*muimg) >> 3); - if (value < 0) value = 0; - else if (value > 255) value = 255; - *uimg = (unsigned char) value; - - value = (short) ((*mvimg) >> 3); - if (value < 0) value = 0; - else if (value > 255) value = 255; - *vimg = (unsigned char) value; - - b[j][i] = false; - - } - else - { // set border color in here - *yimg = (unsigned char) 96; - *uimg = (unsigned char) 128; - *vimg = (unsigned char) 128; - - b[j][i] = true; - } - - yimg++; - uimg++; - vimg++; - myimg++; - muimg++; - mvimg++; - } - } - - if(m_wb.horizontal) - { - //Scan through each row and increment top if the row contains any gray - for (j = 0; j < imgMos.Y.height; j++) - { - for (i = cropping_rect.left; i < cropping_rect.right; i++) - { - if (b[j][i]) - { - break; // to next row - } - } - - if (i == cropping_rect.right) //no gray pixel in this row! - { - cropping_rect.top = j; - break; - } - } - - //Scan through each row and decrement bottom if the row contains any gray - for (j = imgMos.Y.height-1; j >= 0; j--) - { - for (i = cropping_rect.left; i < cropping_rect.right; i++) - { - if (b[j][i]) - { - break; // to next row - } - } - - if (i == cropping_rect.right) //no gray pixel in this row! - { - cropping_rect.bottom = j; - break; - } - } - } - else // Vertical Mosaic - { - //Scan through each column and increment left if the column contains any gray - for (i = 0; i < imgMos.Y.width; i++) - { - for (j = cropping_rect.top; j < cropping_rect.bottom; j++) - { - if (b[j][i]) - { - break; // to next column - } - } - - if (j == cropping_rect.bottom) //no gray pixel in this column! - { - cropping_rect.left = i; - break; - } - } - - //Scan through each column and decrement right if the column contains any gray - for (i = imgMos.Y.width-1; i >= 0; i--) - { - for (j = cropping_rect.top; j < cropping_rect.bottom; j++) - { - if (b[j][i]) - { - break; // to next column - } - } - - if (j == cropping_rect.bottom) //no gray pixel in this column! - { - cropping_rect.right = i; - break; - } - } - - } - - RoundingCroppingSizeToMultipleOf8(cropping_rect); - - for(int j=0; j<imgMos.Y.height; j++) - { - delete b[j]; - } - - delete b; - - return BLEND_RET_OK; -} - -void Blend::RoundingCroppingSizeToMultipleOf8(MosaicRect &rect) { - int height = rect.bottom - rect.top + 1; - int residue = height & 7; - rect.bottom -= residue; - - int width = rect.right - rect.left + 1; - residue = width & 7; - rect.right -= residue; -} - -void Blend::ComputeMask(CSite *csite, BlendRect &vcrect, BlendRect &brect, MosaicRect &rect, YUVinfo &imgMos, int site_idx) -{ - PyramidShort *dptr = m_pMosaicYPyr; - - int nC = m_wb.nlevsC; - int l = (int) ((vcrect.lft - rect.left)); - int b = (int) ((vcrect.bot - rect.top)); - int r = (int) ((vcrect.rgt - rect.left)); - int t = (int) ((vcrect.top - rect.top)); - - if (vcrect.lft == brect.lft) - l = (l <= 0) ? -BORDER : l - BORDER; - else if (l < -BORDER) - l = -BORDER; - - if (vcrect.bot == brect.bot) - b = (b <= 0) ? -BORDER : b - BORDER; - else if (b < -BORDER) - b = -BORDER; - - if (vcrect.rgt == brect.rgt) - r = (r >= dptr->width) ? dptr->width + BORDER - 1 : r + BORDER; - else if (r >= dptr->width + BORDER) - r = dptr->width + BORDER - 1; - - if (vcrect.top == brect.top) - t = (t >= dptr->height) ? dptr->height + BORDER - 1 : t + BORDER; - else if (t >= dptr->height + BORDER) - t = dptr->height + BORDER - 1; - - // Walk the Region of interest and populate the pyramid - for (int j = b; j <= t; j++) - { - int jj = j; - double sj = jj + rect.top; - - for (int i = l; i <= r; i++) - { - int ii = i; - // project point and then triangulate to neighbors - double si = ii + rect.left; - - double dself = hypotSq(csite->getVCenter().x - si, csite->getVCenter().y - sj); - int inMask = ((unsigned) ii < imgMos.Y.width && - (unsigned) jj < imgMos.Y.height) ? 1 : 0; - - if(!inMask) - continue; - - // scan the neighbors to see if this is a valid position - unsigned char mask = (unsigned char) 255; - SEdgeVector *ce; - int ecnt; - for (ce = csite->getNeighbor(), ecnt = csite->getNumNeighbors(); ecnt--; ce++) - { - double d1 = hypotSq(m_AllSites[ce->second].getVCenter().x - si, - m_AllSites[ce->second].getVCenter().y - sj); - if (d1 < dself) - { - break; - } - } - - if (ecnt >= 0) continue; - - imgMos.Y.ptr[jj][ii] = (unsigned char)site_idx; - } - } -} - -void Blend::ProcessPyramidForThisFrame(CSite *csite, BlendRect &vcrect, BlendRect &brect, MosaicRect &rect, YUVinfo &imgMos, double trs[3][3], int site_idx) -{ - // Put the Region of interest (for all levels) into m_pMosaicYPyr - double inv_trs[3][3]; - inv33d(trs, inv_trs); - - // Process each pyramid level - PyramidShort *sptr = m_pFrameYPyr; - PyramidShort *suptr = m_pFrameUPyr; - PyramidShort *svptr = m_pFrameVPyr; - - PyramidShort *dptr = m_pMosaicYPyr; - PyramidShort *duptr = m_pMosaicUPyr; - PyramidShort *dvptr = m_pMosaicVPyr; - - int dscale = 0; // distance scale for the current level - int nC = m_wb.nlevsC; - for (int n = m_wb.nlevs; n--; dscale++, dptr++, sptr++, dvptr++, duptr++, svptr++, suptr++, nC--) - { - int l = (int) ((vcrect.lft - rect.left) / (1 << dscale)); - int b = (int) ((vcrect.bot - rect.top) / (1 << dscale)); - int r = (int) ((vcrect.rgt - rect.left) / (1 << dscale) + .5); - int t = (int) ((vcrect.top - rect.top) / (1 << dscale) + .5); - - if (vcrect.lft == brect.lft) - l = (l <= 0) ? -BORDER : l - BORDER; - else if (l < -BORDER) - l = -BORDER; - - if (vcrect.bot == brect.bot) - b = (b <= 0) ? -BORDER : b - BORDER; - else if (b < -BORDER) - b = -BORDER; - - if (vcrect.rgt == brect.rgt) - r = (r >= dptr->width) ? dptr->width + BORDER - 1 : r + BORDER; - else if (r >= dptr->width + BORDER) - r = dptr->width + BORDER - 1; - - if (vcrect.top == brect.top) - t = (t >= dptr->height) ? dptr->height + BORDER - 1 : t + BORDER; - else if (t >= dptr->height + BORDER) - t = dptr->height + BORDER - 1; - - // Walk the Region of interest and populate the pyramid - for (int j = b; j <= t; j++) - { - int jj = (j << dscale); - double sj = jj + rect.top; - - for (int i = l; i <= r; i++) - { - int ii = (i << dscale); - // project point and then triangulate to neighbors - double si = ii + rect.left; - - int inMask = ((unsigned) ii < imgMos.Y.width && - (unsigned) jj < imgMos.Y.height) ? 1 : 0; - - if(inMask && imgMos.Y.ptr[jj][ii] != site_idx && - imgMos.V.ptr[jj][ii] != site_idx && - imgMos.Y.ptr[jj][ii] != 255) - continue; - - // Setup weights for cross-fading - // Weight of the intensity already in the output pixel - double wt0 = 0.0; - // Weight of the intensity from the input pixel (current frame) - double wt1 = 1.0; - - if (m_wb.stripType == STRIP_TYPE_WIDE) - { - if(inMask && imgMos.Y.ptr[jj][ii] != 255) - { - // If not on a seam OR pyramid level exceeds - // maximum level for cross-fading. - if((imgMos.V.ptr[jj][ii] == 128) || - (dscale > STRIP_CROSS_FADE_MAX_PYR_LEVEL)) - { - wt0 = 0.0; - wt1 = 1.0; - } - else - { - wt0 = 1.0; - wt1 = ((imgMos.Y.ptr[jj][ii] == site_idx) ? - (double)imgMos.U.ptr[jj][ii] / 100.0 : - 1.0 - (double)imgMos.U.ptr[jj][ii] / 100.0); - } - } - } - - // Project this mosaic point into the original frame coordinate space - double xx, yy; - - MosaicToFrame(inv_trs, si, sj, xx, yy); - - if (xx < 0.0 || yy < 0.0 || xx > width - 1.0 || yy > height - 1.0) - { - if(inMask) - { - imgMos.Y.ptr[jj][ii] = 255; - wt0 = 0.0f; - wt1 = 1.0f; - } - } - - xx /= (1 << dscale); - yy /= (1 << dscale); - - - int x1 = (xx >= 0.0) ? (int) xx : (int) floor(xx); - int y1 = (yy >= 0.0) ? (int) yy : (int) floor(yy); - - // Final destination in extended pyramid -#ifndef LINEAR_INTERP - if(inSegment(x1, sptr->width, BORDER-1) && - inSegment(y1, sptr->height, BORDER-1)) - { - double xfrac = xx - x1; - double yfrac = yy - y1; - dptr->ptr[j][i] = (short) (wt0 * dptr->ptr[j][i] + .5 + - wt1 * ciCalc(sptr, x1, y1, xfrac, yfrac)); - if (dvptr >= m_pMosaicVPyr && nC > 0) - { - duptr->ptr[j][i] = (short) (wt0 * duptr->ptr[j][i] + .5 + - wt1 * ciCalc(suptr, x1, y1, xfrac, yfrac)); - dvptr->ptr[j][i] = (short) (wt0 * dvptr->ptr[j][i] + .5 + - wt1 * ciCalc(svptr, x1, y1, xfrac, yfrac)); - } - } -#else - if(inSegment(x1, sptr->width, BORDER) && inSegment(y1, sptr->height, BORDER)) - { - int x2 = x1 + 1; - int y2 = y1 + 1; - double xfrac = xx - x1; - double yfrac = yy - y1; - double y1val = sptr->ptr[y1][x1] + - (sptr->ptr[y1][x2] - sptr->ptr[y1][x1]) * xfrac; - double y2val = sptr->ptr[y2][x1] + - (sptr->ptr[y2][x2] - sptr->ptr[y2][x1]) * xfrac; - dptr->ptr[j][i] = (short) (y1val + yfrac * (y2val - y1val)); - - if (dvptr >= m_pMosaicVPyr && nC > 0) - { - y1val = suptr->ptr[y1][x1] + - (suptr->ptr[y1][x2] - suptr->ptr[y1][x1]) * xfrac; - y2val = suptr->ptr[y2][x1] + - (suptr->ptr[y2][x2] - suptr->ptr[y2][x1]) * xfrac; - - duptr->ptr[j][i] = (short) (y1val + yfrac * (y2val - y1val)); - - y1val = svptr->ptr[y1][x1] + - (svptr->ptr[y1][x2] - svptr->ptr[y1][x1]) * xfrac; - y2val = svptr->ptr[y2][x1] + - (svptr->ptr[y2][x2] - svptr->ptr[y2][x1]) * xfrac; - - dvptr->ptr[j][i] = (short) (y1val + yfrac * (y2val - y1val)); - } - } -#endif - else - { - clipToSegment(x1, sptr->width, BORDER); - clipToSegment(y1, sptr->height, BORDER); - - dptr->ptr[j][i] = (short) (wt0 * dptr->ptr[j][i] + 0.5 + - wt1 * sptr->ptr[y1][x1] ); - if (dvptr >= m_pMosaicVPyr && nC > 0) - { - dvptr->ptr[j][i] = (short) (wt0 * dvptr->ptr[j][i] + - 0.5 + wt1 * svptr->ptr[y1][x1] ); - duptr->ptr[j][i] = (short) (wt0 * duptr->ptr[j][i] + - 0.5 + wt1 * suptr->ptr[y1][x1] ); - } - } - } - } - } -} - -void Blend::MosaicToFrame(double trs[3][3], double x, double y, double &wx, double &wy) -{ - double X, Y, z; - if (m_wb.theta == 0.0) - { - X = x; - Y = y; - } - else if (m_wb.horizontal) - { - double alpha = x * m_wb.direction / m_wb.width; - double length = (y - alpha * m_wb.correction) * m_wb.direction + m_wb.radius; - double deltaTheta = m_wb.theta * alpha; - double sinTheta = sin(deltaTheta); - double cosTheta = sqrt(1.0 - sinTheta * sinTheta) * m_wb.direction; - X = length * sinTheta + m_wb.x; - Y = length * cosTheta + m_wb.y; - } - else - { - double alpha = y * m_wb.direction / m_wb.width; - double length = (x - alpha * m_wb.correction) * m_wb.direction + m_wb.radius; - double deltaTheta = m_wb.theta * alpha; - double sinTheta = sin(deltaTheta); - double cosTheta = sqrt(1.0 - sinTheta * sinTheta) * m_wb.direction; - Y = length * sinTheta + m_wb.y; - X = length * cosTheta + m_wb.x; - } - z = ProjZ(trs, X, Y, 1.0); - wx = ProjX(trs, X, Y, z, 1.0); - wy = ProjY(trs, X, Y, z, 1.0); -} - -void Blend::FrameToMosaic(double trs[3][3], double x, double y, double &wx, double &wy) -{ - // Project into the intermediate Mosaic coordinate system - double z = ProjZ(trs, x, y, 1.0); - double X = ProjX(trs, x, y, z, 1.0); - double Y = ProjY(trs, x, y, z, 1.0); - - if (m_wb.theta == 0.0) - { - // No rotation, then this is all we need to do. - wx = X; - wy = Y; - } - else if (m_wb.horizontal) - { - double deltaX = X - m_wb.x; - double deltaY = Y - m_wb.y; - double length = sqrt(deltaX * deltaX + deltaY * deltaY); - double deltaTheta = asin(deltaX / length); - double alpha = deltaTheta / m_wb.theta; - wx = alpha * m_wb.width * m_wb.direction; - wy = (length - m_wb.radius) * m_wb.direction + alpha * m_wb.correction; - } - else - { - double deltaX = X - m_wb.x; - double deltaY = Y - m_wb.y; - double length = sqrt(deltaX * deltaX + deltaY * deltaY); - double deltaTheta = asin(deltaY / length); - double alpha = deltaTheta / m_wb.theta; - wy = alpha * m_wb.width * m_wb.direction; - wx = (length - m_wb.radius) * m_wb.direction + alpha * m_wb.correction; - } -} - - - -// Clip the region of interest as small as possible by using the Voronoi edges of -// the neighbors -void Blend::ClipBlendRect(CSite *csite, BlendRect &brect) -{ - SEdgeVector *ce; - int ecnt; - for (ce = csite->getNeighbor(), ecnt = csite->getNumNeighbors(); ecnt--; ce++) - { - // calculate the Voronoi bisector intersection - const double epsilon = 1e-5; - double dx = (m_AllSites[ce->second].getVCenter().x - m_AllSites[ce->first].getVCenter().x); - double dy = (m_AllSites[ce->second].getVCenter().y - m_AllSites[ce->first].getVCenter().y); - double xmid = m_AllSites[ce->first].getVCenter().x + dx/2.0; - double ymid = m_AllSites[ce->first].getVCenter().y + dy/2.0; - double inter; - - if (dx > epsilon) - { - // neighbor is on right - if ((inter = m_wb.roundoffOverlap + xmid - dy * (((dy >= 0.0) ? brect.bot : brect.top) - ymid) / dx) < brect.rgt) - brect.rgt = inter; - } - else if (dx < -epsilon) - { - // neighbor is on left - if ((inter = -m_wb.roundoffOverlap + xmid - dy * (((dy >= 0.0) ? brect.bot : brect.top) - ymid) / dx) > brect.lft) - brect.lft = inter; - } - if (dy > epsilon) - { - // neighbor is above - if ((inter = m_wb.roundoffOverlap + ymid - dx * (((dx >= 0.0) ? brect.lft : brect.rgt) - xmid) / dy) < brect.top) - brect.top = inter; - } - else if (dy < -epsilon) - { - // neighbor is below - if ((inter = -m_wb.roundoffOverlap + ymid - dx * (((dx >= 0.0) ? brect.lft : brect.rgt) - xmid) / dy) > brect.bot) - brect.bot = inter; - } - } -} - -void Blend::FrameToMosaicRect(int width, int height, double trs[3][3], BlendRect &brect) -{ - // We need to walk the perimeter since the borders can be bent. - brect.lft = brect.bot = 2e30; - brect.rgt = brect.top = -2e30; - double xpos, ypos; - double lasty = height - 1.0; - double lastx = width - 1.0; - int i; - - for (i = width; i--;) - { - - FrameToMosaic(trs, (double) i, 0.0, xpos, ypos); - ClipRect(xpos, ypos, brect); - FrameToMosaic(trs, (double) i, lasty, xpos, ypos); - ClipRect(xpos, ypos, brect); - } - for (i = height; i--;) - { - FrameToMosaic(trs, 0.0, (double) i, xpos, ypos); - ClipRect(xpos, ypos, brect); - FrameToMosaic(trs, lastx, (double) i, xpos, ypos); - ClipRect(xpos, ypos, brect); - } -} - -void Blend::SelectRelevantFrames(MosaicFrame **frames, int frames_size, - MosaicFrame **relevant_frames, int &relevant_frames_size) -{ - MosaicFrame *first = frames[0]; - MosaicFrame *last = frames[frames_size-1]; - MosaicFrame *mb; - - double fxpos = first->trs[0][2], fypos = first->trs[1][2]; - - double midX = last->width / 2.0; - double midY = last->height / 2.0; - double z = ProjZ(first->trs, midX, midY, 1.0); - double firstX, firstY; - double prevX = firstX = ProjX(first->trs, midX, midY, z, 1.0); - double prevY = firstY = ProjY(first->trs, midX, midY, z, 1.0); - - relevant_frames[0] = first; // Add first frame by default - relevant_frames_size = 1; - - for (int i = 0; i < frames_size - 1; i++) - { - mb = frames[i]; - double currX, currY; - z = ProjZ(mb->trs, midX, midY, 1.0); - currX = ProjX(mb->trs, midX, midY, z, 1.0); - currY = ProjY(mb->trs, midX, midY, z, 1.0); - double deltaX = currX - prevX; - double deltaY = currY - prevY; - double center2centerDist = sqrt(deltaY * deltaY + deltaX * deltaX); - - if (fabs(deltaX) > STRIP_SEPARATION_THRESHOLD_PXLS || - fabs(deltaY) > STRIP_SEPARATION_THRESHOLD_PXLS) - { - relevant_frames[relevant_frames_size] = mb; - relevant_frames_size++; - - prevX = currX; - prevY = currY; - } - } - - // Add last frame by default - relevant_frames[relevant_frames_size] = last; - relevant_frames_size++; -} - -void Blend::ComputeBlendParameters(MosaicFrame **frames, int frames_size, int is360) -{ - // For FULL and PAN modes, we do not unwarp the mosaic into a rectangular coordinate system - // and so we set the theta to 0 and return. - if (m_wb.blendingType != BLEND_TYPE_CYLPAN && m_wb.blendingType != BLEND_TYPE_HORZ) - { - m_wb.theta = 0.0; - return; - } - - MosaicFrame *first = frames[0]; - MosaicFrame *last = frames[frames_size-1]; - MosaicFrame *mb; - - double lxpos = last->trs[0][2], lypos = last->trs[1][2]; - double fxpos = first->trs[0][2], fypos = first->trs[1][2]; - - // Calculate warp to produce proper stitching. - // get x, y displacement - double midX = last->width / 2.0; - double midY = last->height / 2.0; - double z = ProjZ(first->trs, midX, midY, 1.0); - double firstX, firstY; - double prevX = firstX = ProjX(first->trs, midX, midY, z, 1.0); - double prevY = firstY = ProjY(first->trs, midX, midY, z, 1.0); - - double arcLength, lastTheta; - m_wb.theta = lastTheta = arcLength = 0.0; - - // Step through all the frames to compute the total arc-length of the cone - // swept while capturing the mosaic (in the original conical coordinate system). - for (int i = 0; i < frames_size; i++) - { - mb = frames[i]; - double currX, currY; - z = ProjZ(mb->trs, midX, midY, 1.0); - currX = ProjX(mb->trs, midX, midY, z, 1.0); - currY = ProjY(mb->trs, midX, midY, z, 1.0); - double deltaX = currX - prevX; - double deltaY = currY - prevY; - - // The arcLength is computed by summing the lengths of the chords - // connecting the pairwise projected image centers of the input image frames. - arcLength += sqrt(deltaY * deltaY + deltaX * deltaX); - - if (!is360) - { - double thisTheta = asin(mb->trs[1][0]); - m_wb.theta += thisTheta - lastTheta; - lastTheta = thisTheta; - } - - prevX = currX; - prevY = currY; - } - - // Stretch this to end at the proper alignment i.e. the width of the - // rectangle is determined by the arcLength computed above and the cone - // sector angle is determined using the rotation of the last frame. - m_wb.width = arcLength; - if (is360) m_wb.theta = asin(last->trs[1][0]); - - // If there is no rotation, we're done. - if (m_wb.theta != 0.0) - { - double dx = prevX - firstX; - double dy = prevY - firstY; - - // If the mosaic was captured by sweeping horizontally - if (abs(lxpos - fxpos) > abs(lypos - fypos)) - { - m_wb.horizontal = 1; - // Calculate radius position to make ends exactly the same Y offset - double radiusTheta = dx / cos(3.14159 / 2.0 - m_wb.theta); - m_wb.radius = dy + radiusTheta * cos(m_wb.theta); - if (m_wb.radius < 0.0) m_wb.radius = -m_wb.radius; - } - else - { - m_wb.horizontal = 0; - // Calculate radius position to make ends exactly the same Y offset - double radiusTheta = dy / cos(3.14159 / 2.0 - m_wb.theta); - m_wb.radius = dx + radiusTheta * cos(m_wb.theta); - if (m_wb.radius < 0.0) m_wb.radius = -m_wb.radius; - } - - // Determine major direction - if (m_wb.horizontal) - { - // Horizontal strip - // m_wb.x,y record the origin of the rectangle coordinate system. - if (is360) m_wb.x = firstX; - else - { - if (lxpos - fxpos < 0) - { - m_wb.x = firstX + midX; - z = ProjZ(last->trs, 0.0, midY, 1.0); - prevX = ProjX(last->trs, 0.0, midY, z, 1.0); - prevY = ProjY(last->trs, 0.0, midY, z, 1.0); - } - else - { - m_wb.x = firstX - midX; - z = ProjZ(last->trs, last->width - 1.0, midY, 1.0); - prevX = ProjX(last->trs, last->width - 1.0, midY, z, 1.0); - prevY = ProjY(last->trs, last->width - 1.0, midY, z, 1.0); - } - } - dy = prevY - firstY; - if (dy < 0.0) m_wb.direction = 1.0; - else m_wb.direction = -1.0; - m_wb.y = firstY - m_wb.radius * m_wb.direction; - if (dy * m_wb.theta > 0.0) m_wb.width = -m_wb.width; - } - else - { - // Vertical strip - if (is360) m_wb.y = firstY; - else - { - if (lypos - fypos < 0) - { - m_wb.x = firstY + midY; - z = ProjZ(last->trs, midX, 0.0, 1.0); - prevX = ProjX(last->trs, midX, 0.0, z, 1.0); - prevY = ProjY(last->trs, midX, 0.0, z, 1.0); - } - else - { - m_wb.x = firstX - midX; - z = ProjZ(last->trs, midX, last->height - 1.0, 1.0); - prevX = ProjX(last->trs, midX, last->height - 1.0, z, 1.0); - prevY = ProjY(last->trs, midX, last->height - 1.0, z, 1.0); - } - } - dx = prevX - firstX; - if (dx < 0.0) m_wb.direction = 1.0; - else m_wb.direction = -1.0; - m_wb.x = firstX - m_wb.radius * m_wb.direction; - if (dx * m_wb.theta > 0.0) m_wb.width = -m_wb.width; - } - - // Calculate the correct correction factor - double deltaX = prevX - m_wb.x; - double deltaY = prevY - m_wb.y; - double length = sqrt(deltaX * deltaX + deltaY * deltaY); - double deltaTheta = (m_wb.horizontal) ? deltaX : deltaY; - deltaTheta = asin(deltaTheta / length); - m_wb.correction = ((m_wb.radius - length) * m_wb.direction) / - (deltaTheta / m_wb.theta); - } -} |