Blend.cpp revision 0a039136e8e46ddbcb45b55e92d80ddb2ddfc2c2
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/////////////////////////////////////////////////// 18// Blend.cpp 19// $Id: Blend.cpp,v 1.22 2011/06/24 04:22:14 mbansal Exp $ 20 21#include <string.h> 22 23#include "Interp.h" 24#include "Blend.h" 25 26#include "Geometry.h" 27#include "trsMatrix.h" 28 29#include "Log.h" 30#define LOG_TAG "BLEND" 31 32Blend::Blend() 33{ 34 m_wb.blendingType = BLEND_TYPE_NONE; 35} 36 37Blend::~Blend() 38{ 39 if (m_pFrameVPyr) free(m_pFrameVPyr); 40 if (m_pFrameUPyr) free(m_pFrameUPyr); 41 if (m_pFrameYPyr) free(m_pFrameYPyr); 42} 43 44int Blend::initialize(int blendingType, int stripType, int frame_width, int frame_height) 45{ 46 this->width = frame_width; 47 this->height = frame_height; 48 this->m_wb.blendingType = blendingType; 49 this->m_wb.stripType = stripType; 50 51 m_wb.blendRange = m_wb.blendRangeUV = BLEND_RANGE_DEFAULT; 52 m_wb.nlevs = m_wb.blendRange; 53 m_wb.nlevsC = m_wb.blendRangeUV; 54 55 if (m_wb.nlevs <= 0) m_wb.nlevs = 1; // Need levels for YUV processing 56 if (m_wb.nlevsC > m_wb.nlevs) m_wb.nlevsC = m_wb.nlevs; 57 58 m_wb.roundoffOverlap = 1.5; 59 60 m_pFrameYPyr = NULL; 61 m_pFrameUPyr = NULL; 62 m_pFrameVPyr = NULL; 63 64 m_pFrameYPyr = PyramidShort::allocatePyramidPacked(m_wb.nlevs, (unsigned short) width, (unsigned short) height, BORDER); 65 m_pFrameUPyr = PyramidShort::allocatePyramidPacked(m_wb.nlevsC, (unsigned short) (width), (unsigned short) (height), BORDER); 66 m_pFrameVPyr = PyramidShort::allocatePyramidPacked(m_wb.nlevsC, (unsigned short) (width), (unsigned short) (height), BORDER); 67 68 if (!m_pFrameYPyr || !m_pFrameUPyr || !m_pFrameVPyr) 69 { 70 LOGE("Error: Could not allocate pyramids for blending"); 71 return BLEND_RET_ERROR_MEMORY; 72 } 73 74 return BLEND_RET_OK; 75} 76 77inline double max(double a, double b) { return a > b ? a : b; } 78inline double min(double a, double b) { return a < b ? a : b; } 79 80void Blend::AlignToMiddleFrame(MosaicFrame **frames, int frames_size) 81{ 82 // Unwarp this frame and Warp the others to match 83 MosaicFrame *mb = NULL; 84 MosaicFrame *ref = frames[int(frames_size/2)]; // Middle frame 85 86 double invtrs[3][3]; 87 inv33d(ref->trs, invtrs); 88 89 for(int mfit = 0; mfit < frames_size; mfit++) 90 { 91 mb = frames[mfit]; 92 double temp[3][3]; 93 mult33d(temp, invtrs, mb->trs); 94 memcpy(mb->trs, temp, sizeof(temp)); 95 normProjMat33d(mb->trs); 96 } 97} 98 99int Blend::runBlend(MosaicFrame **oframes, MosaicFrame **rframes, 100 int frames_size, 101 ImageType &imageMosaicYVU, int &mosaicWidth, int &mosaicHeight, 102 float &progress, bool &cancelComputation) 103{ 104 int ret; 105 int numCenters; 106 107 MosaicFrame **frames; 108 109 // For THIN strip mode, accept all frames for blending 110 if (m_wb.stripType == STRIP_TYPE_THIN) 111 { 112 frames = oframes; 113 } 114 else // For WIDE strip mode, first select the relevant frames to blend. 115 { 116 SelectRelevantFrames(oframes, frames_size, rframes, frames_size); 117 frames = rframes; 118 } 119 120 ComputeBlendParameters(frames, frames_size, true); 121 numCenters = frames_size; 122 123 if (numCenters == 0) 124 { 125 LOGE("Error: No frames to blend"); 126 return BLEND_RET_ERROR; 127 } 128 129 if (!(m_AllSites = m_Triangulator.allocMemory(numCenters))) 130 { 131 return BLEND_RET_ERROR_MEMORY; 132 } 133 134 // Bounding rectangle (real numbers) of the final mosaic computed by projecting 135 // each input frame into the mosaic coordinate system. 136 BlendRect global_rect; 137 138 global_rect.lft = global_rect.bot = 2e30; // min values 139 global_rect.rgt = global_rect.top = -2e30; // max values 140 MosaicFrame *mb = NULL; 141 double halfwidth = width / 2.0; 142 double halfheight = height / 2.0; 143 144 double z, x0, y0, x1, y1, x2, y2, x3, y3; 145 146 // Corners of the left-most and right-most frames respectively in the 147 // mosaic coordinate system. 148 double xLeftCorners[2] = {2e30, 2e30}; 149 double xRightCorners[2] = {-2e30, -2e30}; 150 151 // Corners of the top-most and bottom-most frames respectively in the 152 // mosaic coordinate system. 153 double yTopCorners[2] = {2e30, 2e30}; 154 double yBottomCorners[2] = {-2e30, -2e30}; 155 156 157 // Determine the extents of the final mosaic 158 CSite *csite = m_AllSites ; 159 for(int mfit = 0; mfit < frames_size; mfit++) 160 { 161 mb = frames[mfit]; 162 163 // Compute clipping for this frame's rect 164 FrameToMosaicRect(mb->width, mb->height, mb->trs, mb->brect); 165 // Clip global rect using this frame's rect 166 ClipRect(mb->brect, global_rect); 167 168 // Calculate the corner points 169 FrameToMosaic(mb->trs, 0.0, 0.0, x0, y0); 170 FrameToMosaic(mb->trs, 0.0, mb->height-1.0, x1, y1); 171 FrameToMosaic(mb->trs, mb->width-1.0, mb->height-1.0, x2, y2); 172 FrameToMosaic(mb->trs, mb->width-1.0, 0.0, x3, y3); 173 174 if(x0 < xLeftCorners[0] || x1 < xLeftCorners[1]) // If either of the left corners is lower 175 { 176 xLeftCorners[0] = x0; 177 xLeftCorners[1] = x1; 178 } 179 180 if(x3 > xRightCorners[0] || x2 > xRightCorners[1]) // If either of the right corners is higher 181 { 182 xRightCorners[0] = x3; 183 xRightCorners[1] = x2; 184 } 185 186 if(y0 < yTopCorners[0] || y3 < yTopCorners[1]) // If either of the top corners is lower 187 { 188 yTopCorners[0] = y0; 189 yTopCorners[1] = y3; 190 } 191 192 if(y1 > yBottomCorners[0] || y2 > yBottomCorners[1]) // If either of the bottom corners is higher 193 { 194 yBottomCorners[0] = y1; 195 yBottomCorners[1] = y2; 196 } 197 198 199 // Compute the centroid of the warped region 200 FindQuadCentroid(x0, y0, x1, y1, x2, y2, x3, y3, csite->getVCenter().x, csite->getVCenter().y); 201 202 csite->setMb(mb); 203 csite++; 204 } 205 206 // Get origin and sizes 207 208 // Bounding rectangle (int numbers) of the final mosaic computed by projecting 209 // each input frame into the mosaic coordinate system. 210 MosaicRect fullRect; 211 212 fullRect.left = (int) floor(global_rect.lft); // min-x 213 fullRect.top = (int) floor(global_rect.bot); // min-y 214 fullRect.right = (int) ceil(global_rect.rgt); // max-x 215 fullRect.bottom = (int) ceil(global_rect.top);// max-y 216 Mwidth = (unsigned short) (fullRect.right - fullRect.left + 1); 217 Mheight = (unsigned short) (fullRect.bottom - fullRect.top + 1); 218 219 int xLeftMost, xRightMost; 220 int yTopMost, yBottomMost; 221 222 // Rounding up, so that we don't include the gray border. 223 xLeftMost = max(0, max(xLeftCorners[0], xLeftCorners[1]) - fullRect.left + 1); 224 xRightMost = min(Mwidth - 1, min(xRightCorners[0], xRightCorners[1]) - fullRect.left - 1); 225 226 yTopMost = max(0, max(yTopCorners[0], yTopCorners[1]) - fullRect.top + 1); 227 yBottomMost = min(Mheight - 1, min(yBottomCorners[0], yBottomCorners[1]) - fullRect.top - 1); 228 229 if (xRightMost <= xLeftMost || yBottomMost <= yTopMost) 230 { 231 LOGE("RunBlend: aborting -consistency check failed," 232 "(xLeftMost, xRightMost, yTopMost, yBottomMost): (%d, %d, %d, %d)", 233 xLeftMost, xRightMost, yTopMost, yBottomMost); 234 return BLEND_RET_ERROR; 235 } 236 237 // Make sure image width is multiple of 4 238 Mwidth = (unsigned short) ((Mwidth + 3) & ~3); 239 Mheight = (unsigned short) ((Mheight + 3) & ~3); // Round up. 240 241 ret = MosaicSizeCheck(LIMIT_SIZE_MULTIPLIER, LIMIT_HEIGHT_MULTIPLIER); 242 if (ret != BLEND_RET_OK) 243 { 244 LOGE("RunBlend: aborting - mosaic size check failed, " 245 "(frame_width, frame_height) vs (mosaic_width, mosaic_height): " 246 "(%d, %d) vs (%d, %d)", width, height, Mwidth, Mheight); 247 return ret; 248 } 249 250 LOGI("Allocate mosaic image for blending - size: %d x %d", Mwidth, Mheight); 251 YUVinfo *imgMos = YUVinfo::allocateImage(Mwidth, Mheight); 252 if (imgMos == NULL) 253 { 254 LOGE("RunBlend: aborting - couldn't alloc %d x %d mosaic image", Mwidth, Mheight); 255 return BLEND_RET_ERROR_MEMORY; 256 } 257 258 // Set the Y image to 255 so we can distinguish when frame idx are written to it 259 memset(imgMos->Y.ptr[0], 255, (imgMos->Y.width * imgMos->Y.height)); 260 // Set the v and u images to black 261 memset(imgMos->V.ptr[0], 128, (imgMos->V.width * imgMos->V.height) << 1); 262 263 // Do the triangulation. It returns a sorted list of edges 264 SEdgeVector *edge; 265 int n = m_Triangulator.triangulate(&edge, numCenters, width, height); 266 m_Triangulator.linkNeighbors(edge, n, numCenters); 267 268 // Bounding rectangle that determines the positioning of the rectangle that is 269 // cropped out of the computed mosaic to get rid of the gray borders. 270 MosaicRect cropping_rect; 271 272 if (m_wb.horizontal) 273 { 274 cropping_rect.left = xLeftMost; 275 cropping_rect.right = xRightMost; 276 } 277 else 278 { 279 cropping_rect.top = yTopMost; 280 cropping_rect.bottom = yBottomMost; 281 } 282 283 // Do merging and blending : 284 ret = DoMergeAndBlend(frames, numCenters, width, height, *imgMos, fullRect, 285 cropping_rect, progress, cancelComputation); 286 287 if (m_wb.blendingType == BLEND_TYPE_HORZ) 288 CropFinalMosaic(*imgMos, cropping_rect); 289 290 291 m_Triangulator.freeMemory(); // note: can be called even if delaunay_alloc() wasn't successful 292 293 imageMosaicYVU = imgMos->Y.ptr[0]; 294 295 296 if (m_wb.blendingType == BLEND_TYPE_HORZ) 297 { 298 mosaicWidth = cropping_rect.right - cropping_rect.left + 1; 299 mosaicHeight = cropping_rect.bottom - cropping_rect.top + 1; 300 } 301 else 302 { 303 mosaicWidth = Mwidth; 304 mosaicHeight = Mheight; 305 } 306 307 return ret; 308} 309 310int Blend::MosaicSizeCheck(float sizeMultiplier, float heightMultiplier) { 311 if (Mwidth < width || Mheight < height) { 312 return BLEND_RET_ERROR; 313 } 314 315 if ((Mwidth * Mheight) > (width * height * sizeMultiplier)) { 316 return BLEND_RET_ERROR; 317 } 318 319 // We won't do blending for the cases where users swing the device too much 320 // in the secondary direction. We use a short side to determine the 321 // secondary direction because users may hold the device in landsape 322 // or portrait. 323 int shortSide = min(Mwidth, Mheight); 324 if (shortSide > height * heightMultiplier) { 325 return BLEND_RET_ERROR; 326 } 327 328 return BLEND_RET_OK; 329} 330 331int Blend::FillFramePyramid(MosaicFrame *mb) 332{ 333 ImageType mbY, mbU, mbV; 334 // Lay this image, centered into the temporary buffer 335 mbY = mb->image; 336 mbU = mb->getU(); 337 mbV = mb->getV(); 338 339 int h, w; 340 341 for(h=0; h<height; h++) 342 { 343 ImageTypeShort yptr = m_pFrameYPyr->ptr[h]; 344 ImageTypeShort uptr = m_pFrameUPyr->ptr[h]; 345 ImageTypeShort vptr = m_pFrameVPyr->ptr[h]; 346 347 for(w=0; w<width; w++) 348 { 349 yptr[w] = (short) ((*(mbY++)) << 3); 350 uptr[w] = (short) ((*(mbU++)) << 3); 351 vptr[w] = (short) ((*(mbV++)) << 3); 352 } 353 } 354 355 // Spread the image through the border 356 PyramidShort::BorderSpread(m_pFrameYPyr, BORDER, BORDER, BORDER, BORDER); 357 PyramidShort::BorderSpread(m_pFrameUPyr, BORDER, BORDER, BORDER, BORDER); 358 PyramidShort::BorderSpread(m_pFrameVPyr, BORDER, BORDER, BORDER, BORDER); 359 360 // Generate Laplacian pyramids 361 if (!PyramidShort::BorderReduce(m_pFrameYPyr, m_wb.nlevs) || !PyramidShort::BorderExpand(m_pFrameYPyr, m_wb.nlevs, -1) || 362 !PyramidShort::BorderReduce(m_pFrameUPyr, m_wb.nlevsC) || !PyramidShort::BorderExpand(m_pFrameUPyr, m_wb.nlevsC, -1) || 363 !PyramidShort::BorderReduce(m_pFrameVPyr, m_wb.nlevsC) || !PyramidShort::BorderExpand(m_pFrameVPyr, m_wb.nlevsC, -1)) 364 { 365 LOGE("Error: Could not generate Laplacian pyramids"); 366 return BLEND_RET_ERROR; 367 } 368 else 369 { 370 return BLEND_RET_OK; 371 } 372} 373 374int Blend::DoMergeAndBlend(MosaicFrame **frames, int nsite, 375 int width, int height, YUVinfo &imgMos, MosaicRect &rect, 376 MosaicRect &cropping_rect, float &progress, bool &cancelComputation) 377{ 378 m_pMosaicYPyr = NULL; 379 m_pMosaicUPyr = NULL; 380 m_pMosaicVPyr = NULL; 381 382 m_pMosaicYPyr = PyramidShort::allocatePyramidPacked(m_wb.nlevs,(unsigned short)rect.Width(),(unsigned short)rect.Height(),BORDER); 383 m_pMosaicUPyr = PyramidShort::allocatePyramidPacked(m_wb.nlevsC,(unsigned short)rect.Width(),(unsigned short)rect.Height(),BORDER); 384 m_pMosaicVPyr = PyramidShort::allocatePyramidPacked(m_wb.nlevsC,(unsigned short)rect.Width(),(unsigned short)rect.Height(),BORDER); 385 if (!m_pMosaicYPyr || !m_pMosaicUPyr || !m_pMosaicVPyr) 386 { 387 LOGE("Error: Could not allocate pyramids for blending"); 388 return BLEND_RET_ERROR_MEMORY; 389 } 390 391 MosaicFrame *mb; 392 393 CSite *esite = m_AllSites + nsite; 394 int site_idx; 395 396 // First go through each frame and for each mosaic pixel determine which frame it should come from 397 site_idx = 0; 398 for(CSite *csite = m_AllSites; csite < esite; csite++) 399 { 400 if(cancelComputation) 401 { 402 if (m_pMosaicVPyr) free(m_pMosaicVPyr); 403 if (m_pMosaicUPyr) free(m_pMosaicUPyr); 404 if (m_pMosaicYPyr) free(m_pMosaicYPyr); 405 return BLEND_RET_CANCELLED; 406 } 407 408 mb = csite->getMb(); 409 410 mb->vcrect = mb->brect; 411 ClipBlendRect(csite, mb->vcrect); 412 413 ComputeMask(csite, mb->vcrect, mb->brect, rect, imgMos, site_idx); 414 415 site_idx++; 416 } 417 418 ////////// imgMos.Y, imgMos.V, imgMos.U are used as follows ////////////// 419 ////////////////////// THIN STRIP MODE /////////////////////////////////// 420 421 // imgMos.Y is used to store the index of the image from which each pixel 422 // in the output mosaic can be read out for the thin-strip mode. Thus, 423 // there is no special handling for pixels around the seam. Also, imgMos.Y 424 // is set to 255 wherever we can't get its value from any input image e.g. 425 // in the gray border areas. imgMos.V and imgMos.U are set to 128 for the 426 // thin-strip mode. 427 428 ////////////////////// WIDE STRIP MODE /////////////////////////////////// 429 430 // imgMos.Y is used the same way as the thin-strip mode. 431 // imgMos.V is used to store the index of the neighboring image which 432 // should contribute to the color of an output pixel in a band around 433 // the seam. Thus, in this band, we will crossfade between the color values 434 // from the image index imgMos.Y and image index imgMos.V. imgMos.U is 435 // used to store the weight (multiplied by 100) that each image will 436 // contribute to the blending process. Thus, we start at 99% contribution 437 // from the first image, then go to 50% contribution from each image at 438 // the seam. Then, the contribution from the second image goes up to 99%. 439 440 // For WIDE mode, set the pixel masks to guide the blender to cross-fade 441 // between the images on either side of each seam: 442 if (m_wb.stripType == STRIP_TYPE_WIDE) 443 { 444 if(m_wb.horizontal) 445 { 446 // Set the number of pixels around the seam to cross-fade between 447 // the two component images, 448 int tw = STRIP_CROSS_FADE_WIDTH * width; 449 450 // Proceed with the image index calculation for cross-fading 451 // only if the cross-fading width is larger than 0 452 if (tw > 0) 453 { 454 for(int y = 0; y < imgMos.Y.height; y++) 455 { 456 // Since we compare two adjecant pixels to determine 457 // whether there is a seam, the termination condition of x 458 // is set to imgMos.Y.width - tw, so that x+1 below 459 // won't exceed the imgMos' boundary. 460 for(int x = tw; x < imgMos.Y.width - tw; ) 461 { 462 // Determine where the seam is... 463 if (imgMos.Y.ptr[y][x] != imgMos.Y.ptr[y][x+1] && 464 imgMos.Y.ptr[y][x] != 255 && 465 imgMos.Y.ptr[y][x+1] != 255) 466 { 467 // Find the image indices on both sides of the seam 468 unsigned char idx1 = imgMos.Y.ptr[y][x]; 469 unsigned char idx2 = imgMos.Y.ptr[y][x+1]; 470 471 for (int o = tw; o >= 0; o--) 472 { 473 // Set the image index to use for cross-fading 474 imgMos.V.ptr[y][x - o] = idx2; 475 // Set the intensity weights to use for cross-fading 476 imgMos.U.ptr[y][x - o] = 50 + (99 - 50) * o / tw; 477 } 478 479 for (int o = 1; o <= tw; o++) 480 { 481 // Set the image index to use for cross-fading 482 imgMos.V.ptr[y][x + o] = idx1; 483 // Set the intensity weights to use for cross-fading 484 imgMos.U.ptr[y][x + o] = imgMos.U.ptr[y][x - o]; 485 } 486 487 x += (tw + 1); 488 } 489 else 490 { 491 x++; 492 } 493 } 494 } 495 } 496 } 497 else 498 { 499 // Set the number of pixels around the seam to cross-fade between 500 // the two component images, 501 int tw = STRIP_CROSS_FADE_WIDTH * height; 502 503 // Proceed with the image index calculation for cross-fading 504 // only if the cross-fading width is larger than 0 505 if (tw > 0) 506 { 507 for(int x = 0; x < imgMos.Y.width; x++) 508 { 509 // Since we compare two adjecant pixels to determine 510 // whether there is a seam, the termination condition of y 511 // is set to imgMos.Y.height - tw, so that y+1 below 512 // won't exceed the imgMos' boundary. 513 for(int y = tw; y < imgMos.Y.height - tw; ) 514 { 515 // Determine where the seam is... 516 if (imgMos.Y.ptr[y][x] != imgMos.Y.ptr[y+1][x] && 517 imgMos.Y.ptr[y][x] != 255 && 518 imgMos.Y.ptr[y+1][x] != 255) 519 { 520 // Find the image indices on both sides of the seam 521 unsigned char idx1 = imgMos.Y.ptr[y][x]; 522 unsigned char idx2 = imgMos.Y.ptr[y+1][x]; 523 524 for (int o = tw; o >= 0; o--) 525 { 526 // Set the image index to use for cross-fading 527 imgMos.V.ptr[y - o][x] = idx2; 528 // Set the intensity weights to use for cross-fading 529 imgMos.U.ptr[y - o][x] = 50 + (99 - 50) * o / tw; 530 } 531 532 for (int o = 1; o <= tw; o++) 533 { 534 // Set the image index to use for cross-fading 535 imgMos.V.ptr[y + o][x] = idx1; 536 // Set the intensity weights to use for cross-fading 537 imgMos.U.ptr[y + o][x] = imgMos.U.ptr[y - o][x]; 538 } 539 540 y += (tw + 1); 541 } 542 else 543 { 544 y++; 545 } 546 } 547 } 548 } 549 } 550 551 } 552 553 // Now perform the actual blending using the frame assignment determined above 554 site_idx = 0; 555 for(CSite *csite = m_AllSites; csite < esite; csite++) 556 { 557 if(cancelComputation) 558 { 559 if (m_pMosaicVPyr) free(m_pMosaicVPyr); 560 if (m_pMosaicUPyr) free(m_pMosaicUPyr); 561 if (m_pMosaicYPyr) free(m_pMosaicYPyr); 562 return BLEND_RET_CANCELLED; 563 } 564 565 mb = csite->getMb(); 566 567 568 if(FillFramePyramid(mb)!=BLEND_RET_OK) 569 return BLEND_RET_ERROR; 570 571 ProcessPyramidForThisFrame(csite, mb->vcrect, mb->brect, rect, imgMos, mb->trs, site_idx); 572 573 progress += TIME_PERCENT_BLEND/nsite; 574 575 site_idx++; 576 } 577 578 579 // Blend 580 PerformFinalBlending(imgMos, cropping_rect); 581 582 if (m_pMosaicVPyr) free(m_pMosaicVPyr); 583 if (m_pMosaicUPyr) free(m_pMosaicUPyr); 584 if (m_pMosaicYPyr) free(m_pMosaicYPyr); 585 586 progress += TIME_PERCENT_FINAL; 587 588 return BLEND_RET_OK; 589} 590 591void Blend::CropFinalMosaic(YUVinfo &imgMos, MosaicRect &cropping_rect) 592{ 593 int i, j, k; 594 ImageType yimg; 595 ImageType uimg; 596 ImageType vimg; 597 598 599 yimg = imgMos.Y.ptr[0]; 600 uimg = imgMos.U.ptr[0]; 601 vimg = imgMos.V.ptr[0]; 602 603 k = 0; 604 for (j = cropping_rect.top; j <= cropping_rect.bottom; j++) 605 { 606 for (i = cropping_rect.left; i <= cropping_rect.right; i++) 607 { 608 yimg[k] = yimg[j*imgMos.Y.width+i]; 609 k++; 610 } 611 } 612 for (j = cropping_rect.top; j <= cropping_rect.bottom; j++) 613 { 614 for (i = cropping_rect.left; i <= cropping_rect.right; i++) 615 { 616 yimg[k] = vimg[j*imgMos.Y.width+i]; 617 k++; 618 } 619 } 620 for (j = cropping_rect.top; j <= cropping_rect.bottom; j++) 621 { 622 for (i = cropping_rect.left; i <= cropping_rect.right; i++) 623 { 624 yimg[k] = uimg[j*imgMos.Y.width+i]; 625 k++; 626 } 627 } 628} 629 630int Blend::PerformFinalBlending(YUVinfo &imgMos, MosaicRect &cropping_rect) 631{ 632 if (!PyramidShort::BorderExpand(m_pMosaicYPyr, m_wb.nlevs, 1) || !PyramidShort::BorderExpand(m_pMosaicUPyr, m_wb.nlevsC, 1) || 633 !PyramidShort::BorderExpand(m_pMosaicVPyr, m_wb.nlevsC, 1)) 634 { 635 LOGE("Error: Could not BorderExpand!"); 636 return BLEND_RET_ERROR; 637 } 638 639 ImageTypeShort myimg; 640 ImageTypeShort muimg; 641 ImageTypeShort mvimg; 642 ImageType yimg; 643 ImageType uimg; 644 ImageType vimg; 645 646 int cx = (int)imgMos.Y.width/2; 647 int cy = (int)imgMos.Y.height/2; 648 649 // 2D boolean array that contains true wherever the mosaic image data is 650 // invalid (i.e. in the gray border). 651 bool **b = new bool*[imgMos.Y.height]; 652 653 for(int j=0; j<imgMos.Y.height; j++) 654 { 655 b[j] = new bool[imgMos.Y.width]; 656 } 657 658 // Copy the resulting image into the full image using the mask 659 int i, j; 660 661 yimg = imgMos.Y.ptr[0]; 662 uimg = imgMos.U.ptr[0]; 663 vimg = imgMos.V.ptr[0]; 664 665 for (j = 0; j < imgMos.Y.height; j++) 666 { 667 myimg = m_pMosaicYPyr->ptr[j]; 668 muimg = m_pMosaicUPyr->ptr[j]; 669 mvimg = m_pMosaicVPyr->ptr[j]; 670 671 for (i = 0; i<imgMos.Y.width; i++) 672 { 673 // A final mask was set up previously, 674 // if the value is zero skip it, otherwise replace it. 675 if (*yimg <255) 676 { 677 short value = (short) ((*myimg) >> 3); 678 if (value < 0) value = 0; 679 else if (value > 255) value = 255; 680 *yimg = (unsigned char) value; 681 682 value = (short) ((*muimg) >> 3); 683 if (value < 0) value = 0; 684 else if (value > 255) value = 255; 685 *uimg = (unsigned char) value; 686 687 value = (short) ((*mvimg) >> 3); 688 if (value < 0) value = 0; 689 else if (value > 255) value = 255; 690 *vimg = (unsigned char) value; 691 692 b[j][i] = false; 693 694 } 695 else 696 { // set border color in here 697 *yimg = (unsigned char) 96; 698 *uimg = (unsigned char) 128; 699 *vimg = (unsigned char) 128; 700 701 b[j][i] = true; 702 } 703 704 yimg++; 705 uimg++; 706 vimg++; 707 myimg++; 708 muimg++; 709 mvimg++; 710 } 711 } 712 713 if(m_wb.horizontal) 714 { 715 //Scan through each row and increment top if the row contains any gray 716 for (j = 0; j < imgMos.Y.height; j++) 717 { 718 for (i = cropping_rect.left; i < cropping_rect.right; i++) 719 { 720 if (b[j][i]) 721 { 722 break; // to next row 723 } 724 } 725 726 if (i == cropping_rect.right) //no gray pixel in this row! 727 { 728 cropping_rect.top = j; 729 break; 730 } 731 } 732 733 //Scan through each row and decrement bottom if the row contains any gray 734 for (j = imgMos.Y.height-1; j >= 0; j--) 735 { 736 for (i = cropping_rect.left; i < cropping_rect.right; i++) 737 { 738 if (b[j][i]) 739 { 740 break; // to next row 741 } 742 } 743 744 if (i == cropping_rect.right) //no gray pixel in this row! 745 { 746 cropping_rect.bottom = j; 747 break; 748 } 749 } 750 } 751 else // Vertical Mosaic 752 { 753 //Scan through each column and increment left if the column contains any gray 754 for (i = 0; i < imgMos.Y.width; i++) 755 { 756 for (j = cropping_rect.top; j < cropping_rect.bottom; j++) 757 { 758 if (b[j][i]) 759 { 760 break; // to next column 761 } 762 } 763 764 if (j == cropping_rect.bottom) //no gray pixel in this column! 765 { 766 cropping_rect.left = i; 767 break; 768 } 769 } 770 771 //Scan through each column and decrement right if the column contains any gray 772 for (i = imgMos.Y.width-1; i >= 0; i--) 773 { 774 for (j = cropping_rect.top; j < cropping_rect.bottom; j++) 775 { 776 if (b[j][i]) 777 { 778 break; // to next column 779 } 780 } 781 782 if (j == cropping_rect.bottom) //no gray pixel in this column! 783 { 784 cropping_rect.right = i; 785 break; 786 } 787 } 788 } 789 790 for(int j=0; j<imgMos.Y.height; j++) 791 { 792 delete b[j]; 793 } 794 795 delete b; 796 797 return BLEND_RET_OK; 798} 799 800void Blend::ComputeMask(CSite *csite, BlendRect &vcrect, BlendRect &brect, MosaicRect &rect, YUVinfo &imgMos, int site_idx) 801{ 802 PyramidShort *dptr = m_pMosaicYPyr; 803 804 int nC = m_wb.nlevsC; 805 int l = (int) ((vcrect.lft - rect.left)); 806 int b = (int) ((vcrect.bot - rect.top)); 807 int r = (int) ((vcrect.rgt - rect.left)); 808 int t = (int) ((vcrect.top - rect.top)); 809 810 if (vcrect.lft == brect.lft) 811 l = (l <= 0) ? -BORDER : l - BORDER; 812 else if (l < -BORDER) 813 l = -BORDER; 814 815 if (vcrect.bot == brect.bot) 816 b = (b <= 0) ? -BORDER : b - BORDER; 817 else if (b < -BORDER) 818 b = -BORDER; 819 820 if (vcrect.rgt == brect.rgt) 821 r = (r >= dptr->width) ? dptr->width + BORDER - 1 : r + BORDER; 822 else if (r >= dptr->width + BORDER) 823 r = dptr->width + BORDER - 1; 824 825 if (vcrect.top == brect.top) 826 t = (t >= dptr->height) ? dptr->height + BORDER - 1 : t + BORDER; 827 else if (t >= dptr->height + BORDER) 828 t = dptr->height + BORDER - 1; 829 830 // Walk the Region of interest and populate the pyramid 831 for (int j = b; j <= t; j++) 832 { 833 int jj = j; 834 double sj = jj + rect.top; 835 836 for (int i = l; i <= r; i++) 837 { 838 int ii = i; 839 // project point and then triangulate to neighbors 840 double si = ii + rect.left; 841 842 double dself = hypotSq(csite->getVCenter().x - si, csite->getVCenter().y - sj); 843 int inMask = ((unsigned) ii < imgMos.Y.width && 844 (unsigned) jj < imgMos.Y.height) ? 1 : 0; 845 846 if(!inMask) 847 continue; 848 849 // scan the neighbors to see if this is a valid position 850 unsigned char mask = (unsigned char) 255; 851 SEdgeVector *ce; 852 int ecnt; 853 for (ce = csite->getNeighbor(), ecnt = csite->getNumNeighbors(); ecnt--; ce++) 854 { 855 double d1 = hypotSq(m_AllSites[ce->second].getVCenter().x - si, 856 m_AllSites[ce->second].getVCenter().y - sj); 857 if (d1 < dself) 858 { 859 break; 860 } 861 } 862 863 if (ecnt >= 0) continue; 864 865 imgMos.Y.ptr[jj][ii] = (unsigned char)site_idx; 866 } 867 } 868} 869 870void Blend::ProcessPyramidForThisFrame(CSite *csite, BlendRect &vcrect, BlendRect &brect, MosaicRect &rect, YUVinfo &imgMos, double trs[3][3], int site_idx) 871{ 872 // Put the Region of interest (for all levels) into m_pMosaicYPyr 873 double inv_trs[3][3]; 874 inv33d(trs, inv_trs); 875 876 // Process each pyramid level 877 PyramidShort *sptr = m_pFrameYPyr; 878 PyramidShort *suptr = m_pFrameUPyr; 879 PyramidShort *svptr = m_pFrameVPyr; 880 881 PyramidShort *dptr = m_pMosaicYPyr; 882 PyramidShort *duptr = m_pMosaicUPyr; 883 PyramidShort *dvptr = m_pMosaicVPyr; 884 885 int dscale = 0; // distance scale for the current level 886 int nC = m_wb.nlevsC; 887 for (int n = m_wb.nlevs; n--; dscale++, dptr++, sptr++, dvptr++, duptr++, svptr++, suptr++, nC--) 888 { 889 int l = (int) ((vcrect.lft - rect.left) / (1 << dscale)); 890 int b = (int) ((vcrect.bot - rect.top) / (1 << dscale)); 891 int r = (int) ((vcrect.rgt - rect.left) / (1 << dscale) + .5); 892 int t = (int) ((vcrect.top - rect.top) / (1 << dscale) + .5); 893 894 if (vcrect.lft == brect.lft) 895 l = (l <= 0) ? -BORDER : l - BORDER; 896 else if (l < -BORDER) 897 l = -BORDER; 898 899 if (vcrect.bot == brect.bot) 900 b = (b <= 0) ? -BORDER : b - BORDER; 901 else if (b < -BORDER) 902 b = -BORDER; 903 904 if (vcrect.rgt == brect.rgt) 905 r = (r >= dptr->width) ? dptr->width + BORDER - 1 : r + BORDER; 906 else if (r >= dptr->width + BORDER) 907 r = dptr->width + BORDER - 1; 908 909 if (vcrect.top == brect.top) 910 t = (t >= dptr->height) ? dptr->height + BORDER - 1 : t + BORDER; 911 else if (t >= dptr->height + BORDER) 912 t = dptr->height + BORDER - 1; 913 914 // Walk the Region of interest and populate the pyramid 915 for (int j = b; j <= t; j++) 916 { 917 int jj = (j << dscale); 918 double sj = jj + rect.top; 919 920 for (int i = l; i <= r; i++) 921 { 922 int ii = (i << dscale); 923 // project point and then triangulate to neighbors 924 double si = ii + rect.left; 925 926 int inMask = ((unsigned) ii < imgMos.Y.width && 927 (unsigned) jj < imgMos.Y.height) ? 1 : 0; 928 929 if(inMask && imgMos.Y.ptr[jj][ii] != site_idx && 930 imgMos.V.ptr[jj][ii] != site_idx && 931 imgMos.Y.ptr[jj][ii] != 255) 932 continue; 933 934 // Setup weights for cross-fading 935 // Weight of the intensity already in the output pixel 936 double wt0 = 0.0; 937 // Weight of the intensity from the input pixel (current frame) 938 double wt1 = 1.0; 939 940 if (m_wb.stripType == STRIP_TYPE_WIDE) 941 { 942 if(inMask && imgMos.Y.ptr[jj][ii] != 255) 943 { 944 if(imgMos.V.ptr[jj][ii] == 128) // Not on a seam 945 { 946 wt0 = 0.0; 947 wt1 = 1.0; 948 } 949 else 950 { 951 wt0 = 1.0; 952 wt1 = ((imgMos.Y.ptr[jj][ii] == site_idx) ? 953 (double)imgMos.U.ptr[jj][ii] / 100.0 : 954 1.0 - (double)imgMos.U.ptr[jj][ii] / 100.0); 955 } 956 } 957 } 958 959 // Project this mosaic point into the original frame coordinate space 960 double xx, yy; 961 962 MosaicToFrame(inv_trs, si, sj, xx, yy); 963 964 if (xx < 0.0 || yy < 0.0 || xx > width - 1.0 || yy > height - 1.0) 965 { 966 if(inMask) 967 { 968 imgMos.Y.ptr[jj][ii] = 255; 969 wt0 = 0.0f; 970 wt1 = 1.0f; 971 } 972 } 973 974 xx /= (1 << dscale); 975 yy /= (1 << dscale); 976 977 978 int x1 = (xx >= 0.0) ? (int) xx : (int) floor(xx); 979 int y1 = (yy >= 0.0) ? (int) yy : (int) floor(yy); 980 981 // Final destination in extended pyramid 982#ifndef LINEAR_INTERP 983 if(inSegment(x1, sptr->width, BORDER-1) && 984 inSegment(y1, sptr->height, BORDER-1)) 985 { 986 double xfrac = xx - x1; 987 double yfrac = yy - y1; 988 dptr->ptr[j][i] = (short) (wt0 * dptr->ptr[j][i] + .5 + 989 wt1 * ciCalc(sptr, x1, y1, xfrac, yfrac)); 990 if (dvptr >= m_pMosaicVPyr && nC > 0) 991 { 992 duptr->ptr[j][i] = (short) (wt0 * duptr->ptr[j][i] + .5 + 993 wt1 * ciCalc(suptr, x1, y1, xfrac, yfrac)); 994 dvptr->ptr[j][i] = (short) (wt0 * dvptr->ptr[j][i] + .5 + 995 wt1 * ciCalc(svptr, x1, y1, xfrac, yfrac)); 996 } 997 } 998#else 999 if(inSegment(x1, sptr->width, BORDER) && inSegment(y1, sptr->height, BORDER)) 1000 { 1001 int x2 = x1 + 1; 1002 int y2 = y1 + 1; 1003 double xfrac = xx - x1; 1004 double yfrac = yy - y1; 1005 double y1val = sptr->ptr[y1][x1] + 1006 (sptr->ptr[y1][x2] - sptr->ptr[y1][x1]) * xfrac; 1007 double y2val = sptr->ptr[y2][x1] + 1008 (sptr->ptr[y2][x2] - sptr->ptr[y2][x1]) * xfrac; 1009 dptr->ptr[j][i] = (short) (y1val + yfrac * (y2val - y1val)); 1010 1011 if (dvptr >= m_pMosaicVPyr && nC > 0) 1012 { 1013 y1val = suptr->ptr[y1][x1] + 1014 (suptr->ptr[y1][x2] - suptr->ptr[y1][x1]) * xfrac; 1015 y2val = suptr->ptr[y2][x1] + 1016 (suptr->ptr[y2][x2] - suptr->ptr[y2][x1]) * xfrac; 1017 1018 duptr->ptr[j][i] = (short) (y1val + yfrac * (y2val - y1val)); 1019 1020 y1val = svptr->ptr[y1][x1] + 1021 (svptr->ptr[y1][x2] - svptr->ptr[y1][x1]) * xfrac; 1022 y2val = svptr->ptr[y2][x1] + 1023 (svptr->ptr[y2][x2] - svptr->ptr[y2][x1]) * xfrac; 1024 1025 dvptr->ptr[j][i] = (short) (y1val + yfrac * (y2val - y1val)); 1026 } 1027 } 1028#endif 1029 else 1030 { 1031 clipToSegment(x1, sptr->width, BORDER); 1032 clipToSegment(y1, sptr->height, BORDER); 1033 1034 dptr->ptr[j][i] = (short) (wt0 * dptr->ptr[j][i] + 0.5 + 1035 wt1 * sptr->ptr[y1][x1] ); 1036 if (dvptr >= m_pMosaicVPyr && nC > 0) 1037 { 1038 dvptr->ptr[j][i] = (short) (wt0 * dvptr->ptr[j][i] + 1039 0.5 + wt1 * svptr->ptr[y1][x1] ); 1040 duptr->ptr[j][i] = (short) (wt0 * duptr->ptr[j][i] + 1041 0.5 + wt1 * suptr->ptr[y1][x1] ); 1042 } 1043 } 1044 } 1045 } 1046 } 1047} 1048 1049void Blend::MosaicToFrame(double trs[3][3], double x, double y, double &wx, double &wy) 1050{ 1051 double X, Y, z; 1052 if (m_wb.theta == 0.0) 1053 { 1054 X = x; 1055 Y = y; 1056 } 1057 else if (m_wb.horizontal) 1058 { 1059 double alpha = x * m_wb.direction / m_wb.width; 1060 double length = (y - alpha * m_wb.correction) * m_wb.direction + m_wb.radius; 1061 double deltaTheta = m_wb.theta * alpha; 1062 double sinTheta = sin(deltaTheta); 1063 double cosTheta = sqrt(1.0 - sinTheta * sinTheta) * m_wb.direction; 1064 X = length * sinTheta + m_wb.x; 1065 Y = length * cosTheta + m_wb.y; 1066 } 1067 else 1068 { 1069 double alpha = y * m_wb.direction / m_wb.width; 1070 double length = (x - alpha * m_wb.correction) * m_wb.direction + m_wb.radius; 1071 double deltaTheta = m_wb.theta * alpha; 1072 double sinTheta = sin(deltaTheta); 1073 double cosTheta = sqrt(1.0 - sinTheta * sinTheta) * m_wb.direction; 1074 Y = length * sinTheta + m_wb.y; 1075 X = length * cosTheta + m_wb.x; 1076 } 1077 z = ProjZ(trs, X, Y, 1.0); 1078 wx = ProjX(trs, X, Y, z, 1.0); 1079 wy = ProjY(trs, X, Y, z, 1.0); 1080} 1081 1082void Blend::FrameToMosaic(double trs[3][3], double x, double y, double &wx, double &wy) 1083{ 1084 // Project into the intermediate Mosaic coordinate system 1085 double z = ProjZ(trs, x, y, 1.0); 1086 double X = ProjX(trs, x, y, z, 1.0); 1087 double Y = ProjY(trs, x, y, z, 1.0); 1088 1089 if (m_wb.theta == 0.0) 1090 { 1091 // No rotation, then this is all we need to do. 1092 wx = X; 1093 wy = Y; 1094 } 1095 else if (m_wb.horizontal) 1096 { 1097 double deltaX = X - m_wb.x; 1098 double deltaY = Y - m_wb.y; 1099 double length = sqrt(deltaX * deltaX + deltaY * deltaY); 1100 double deltaTheta = asin(deltaX / length); 1101 double alpha = deltaTheta / m_wb.theta; 1102 wx = alpha * m_wb.width * m_wb.direction; 1103 wy = (length - m_wb.radius) * m_wb.direction + alpha * m_wb.correction; 1104 } 1105 else 1106 { 1107 double deltaX = X - m_wb.x; 1108 double deltaY = Y - m_wb.y; 1109 double length = sqrt(deltaX * deltaX + deltaY * deltaY); 1110 double deltaTheta = asin(deltaY / length); 1111 double alpha = deltaTheta / m_wb.theta; 1112 wy = alpha * m_wb.width * m_wb.direction; 1113 wx = (length - m_wb.radius) * m_wb.direction + alpha * m_wb.correction; 1114 } 1115} 1116 1117 1118 1119// Clip the region of interest as small as possible by using the Voronoi edges of 1120// the neighbors 1121void Blend::ClipBlendRect(CSite *csite, BlendRect &brect) 1122{ 1123 SEdgeVector *ce; 1124 int ecnt; 1125 for (ce = csite->getNeighbor(), ecnt = csite->getNumNeighbors(); ecnt--; ce++) 1126 { 1127 // calculate the Voronoi bisector intersection 1128 const double epsilon = 1e-5; 1129 double dx = (m_AllSites[ce->second].getVCenter().x - m_AllSites[ce->first].getVCenter().x); 1130 double dy = (m_AllSites[ce->second].getVCenter().y - m_AllSites[ce->first].getVCenter().y); 1131 double xmid = m_AllSites[ce->first].getVCenter().x + dx/2.0; 1132 double ymid = m_AllSites[ce->first].getVCenter().y + dy/2.0; 1133 double inter; 1134 1135 if (dx > epsilon) 1136 { 1137 // neighbor is on right 1138 if ((inter = m_wb.roundoffOverlap + xmid - dy * (((dy >= 0.0) ? brect.bot : brect.top) - ymid) / dx) < brect.rgt) 1139 brect.rgt = inter; 1140 } 1141 else if (dx < -epsilon) 1142 { 1143 // neighbor is on left 1144 if ((inter = -m_wb.roundoffOverlap + xmid - dy * (((dy >= 0.0) ? brect.bot : brect.top) - ymid) / dx) > brect.lft) 1145 brect.lft = inter; 1146 } 1147 if (dy > epsilon) 1148 { 1149 // neighbor is above 1150 if ((inter = m_wb.roundoffOverlap + ymid - dx * (((dx >= 0.0) ? brect.lft : brect.rgt) - xmid) / dy) < brect.top) 1151 brect.top = inter; 1152 } 1153 else if (dy < -epsilon) 1154 { 1155 // neighbor is below 1156 if ((inter = -m_wb.roundoffOverlap + ymid - dx * (((dx >= 0.0) ? brect.lft : brect.rgt) - xmid) / dy) > brect.bot) 1157 brect.bot = inter; 1158 } 1159 } 1160} 1161 1162void Blend::FrameToMosaicRect(int width, int height, double trs[3][3], BlendRect &brect) 1163{ 1164 // We need to walk the perimeter since the borders can be bent. 1165 brect.lft = brect.bot = 2e30; 1166 brect.rgt = brect.top = -2e30; 1167 double xpos, ypos; 1168 double lasty = height - 1.0; 1169 double lastx = width - 1.0; 1170 int i; 1171 1172 for (i = width; i--;) 1173 { 1174 1175 FrameToMosaic(trs, (double) i, 0.0, xpos, ypos); 1176 ClipRect(xpos, ypos, brect); 1177 FrameToMosaic(trs, (double) i, lasty, xpos, ypos); 1178 ClipRect(xpos, ypos, brect); 1179 } 1180 for (i = height; i--;) 1181 { 1182 FrameToMosaic(trs, 0.0, (double) i, xpos, ypos); 1183 ClipRect(xpos, ypos, brect); 1184 FrameToMosaic(trs, lastx, (double) i, xpos, ypos); 1185 ClipRect(xpos, ypos, brect); 1186 } 1187} 1188 1189void Blend::SelectRelevantFrames(MosaicFrame **frames, int frames_size, 1190 MosaicFrame **relevant_frames, int &relevant_frames_size) 1191{ 1192 MosaicFrame *first = frames[0]; 1193 MosaicFrame *last = frames[frames_size-1]; 1194 MosaicFrame *mb; 1195 1196 double fxpos = first->trs[0][2], fypos = first->trs[1][2]; 1197 1198 double midX = last->width / 2.0; 1199 double midY = last->height / 2.0; 1200 double z = ProjZ(first->trs, midX, midY, 1.0); 1201 double firstX, firstY; 1202 double prevX = firstX = ProjX(first->trs, midX, midY, z, 1.0); 1203 double prevY = firstY = ProjY(first->trs, midX, midY, z, 1.0); 1204 1205 relevant_frames[0] = first; // Add first frame by default 1206 relevant_frames_size = 1; 1207 1208 for (int i = 0; i < frames_size - 1; i++) 1209 { 1210 mb = frames[i]; 1211 double currX, currY; 1212 z = ProjZ(mb->trs, midX, midY, 1.0); 1213 currX = ProjX(mb->trs, midX, midY, z, 1.0); 1214 currY = ProjY(mb->trs, midX, midY, z, 1.0); 1215 double deltaX = currX - prevX; 1216 double deltaY = currY - prevY; 1217 double center2centerDist = sqrt(deltaY * deltaY + deltaX * deltaX); 1218 1219 if (fabs(deltaX) > STRIP_SEPARATION_THRESHOLD * last->width || 1220 fabs(deltaY) > STRIP_SEPARATION_THRESHOLD * last->height) 1221 { 1222 relevant_frames[relevant_frames_size] = mb; 1223 relevant_frames_size++; 1224 1225 prevX = currX; 1226 prevY = currY; 1227 } 1228 } 1229 1230 // Add last frame by default 1231 relevant_frames[relevant_frames_size] = last; 1232 relevant_frames_size++; 1233} 1234 1235void Blend::ComputeBlendParameters(MosaicFrame **frames, int frames_size, int is360) 1236{ 1237 // For FULL and PAN modes, we do not unwarp the mosaic into a rectangular coordinate system 1238 // and so we set the theta to 0 and return. 1239 if (m_wb.blendingType != BLEND_TYPE_CYLPAN && m_wb.blendingType != BLEND_TYPE_HORZ) 1240 { 1241 m_wb.theta = 0.0; 1242 return; 1243 } 1244 1245 MosaicFrame *first = frames[0]; 1246 MosaicFrame *last = frames[frames_size-1]; 1247 MosaicFrame *mb; 1248 1249 double lxpos = last->trs[0][2], lypos = last->trs[1][2]; 1250 double fxpos = first->trs[0][2], fypos = first->trs[1][2]; 1251 1252 // Calculate warp to produce proper stitching. 1253 // get x, y displacement 1254 double midX = last->width / 2.0; 1255 double midY = last->height / 2.0; 1256 double z = ProjZ(first->trs, midX, midY, 1.0); 1257 double firstX, firstY; 1258 double prevX = firstX = ProjX(first->trs, midX, midY, z, 1.0); 1259 double prevY = firstY = ProjY(first->trs, midX, midY, z, 1.0); 1260 1261 double arcLength, lastTheta; 1262 m_wb.theta = lastTheta = arcLength = 0.0; 1263 1264 // Step through all the frames to compute the total arc-length of the cone 1265 // swept while capturing the mosaic (in the original conical coordinate system). 1266 for (int i = 0; i < frames_size; i++) 1267 { 1268 mb = frames[i]; 1269 double currX, currY; 1270 z = ProjZ(mb->trs, midX, midY, 1.0); 1271 currX = ProjX(mb->trs, midX, midY, z, 1.0); 1272 currY = ProjY(mb->trs, midX, midY, z, 1.0); 1273 double deltaX = currX - prevX; 1274 double deltaY = currY - prevY; 1275 1276 // The arcLength is computed by summing the lengths of the chords 1277 // connecting the pairwise projected image centers of the input image frames. 1278 arcLength += sqrt(deltaY * deltaY + deltaX * deltaX); 1279 1280 if (!is360) 1281 { 1282 double thisTheta = asin(mb->trs[1][0]); 1283 m_wb.theta += thisTheta - lastTheta; 1284 lastTheta = thisTheta; 1285 } 1286 1287 prevX = currX; 1288 prevY = currY; 1289 } 1290 1291 // Stretch this to end at the proper alignment i.e. the width of the 1292 // rectangle is determined by the arcLength computed above and the cone 1293 // sector angle is determined using the rotation of the last frame. 1294 m_wb.width = arcLength; 1295 if (is360) m_wb.theta = asin(last->trs[1][0]); 1296 1297 // If there is no rotation, we're done. 1298 if (m_wb.theta != 0.0) 1299 { 1300 double dx = prevX - firstX; 1301 double dy = prevY - firstY; 1302 1303 // If the mosaic was captured by sweeping horizontally 1304 if (abs(lxpos - fxpos) > abs(lypos - fypos)) 1305 { 1306 m_wb.horizontal = 1; 1307 // Calculate radius position to make ends exactly the same Y offset 1308 double radiusTheta = dx / cos(3.14159 / 2.0 - m_wb.theta); 1309 m_wb.radius = dy + radiusTheta * cos(m_wb.theta); 1310 if (m_wb.radius < 0.0) m_wb.radius = -m_wb.radius; 1311 } 1312 else 1313 { 1314 m_wb.horizontal = 0; 1315 // Calculate radius position to make ends exactly the same Y offset 1316 double radiusTheta = dy / cos(3.14159 / 2.0 - m_wb.theta); 1317 m_wb.radius = dx + radiusTheta * cos(m_wb.theta); 1318 if (m_wb.radius < 0.0) m_wb.radius = -m_wb.radius; 1319 } 1320 1321 // Determine major direction 1322 if (m_wb.horizontal) 1323 { 1324 // Horizontal strip 1325 // m_wb.x,y record the origin of the rectangle coordinate system. 1326 if (is360) m_wb.x = firstX; 1327 else 1328 { 1329 if (lxpos - fxpos < 0) 1330 { 1331 m_wb.x = firstX + midX; 1332 z = ProjZ(last->trs, 0.0, midY, 1.0); 1333 prevX = ProjX(last->trs, 0.0, midY, z, 1.0); 1334 prevY = ProjY(last->trs, 0.0, midY, z, 1.0); 1335 } 1336 else 1337 { 1338 m_wb.x = firstX - midX; 1339 z = ProjZ(last->trs, last->width - 1.0, midY, 1.0); 1340 prevX = ProjX(last->trs, last->width - 1.0, midY, z, 1.0); 1341 prevY = ProjY(last->trs, last->width - 1.0, midY, z, 1.0); 1342 } 1343 } 1344 dy = prevY - firstY; 1345 if (dy < 0.0) m_wb.direction = 1.0; 1346 else m_wb.direction = -1.0; 1347 m_wb.y = firstY - m_wb.radius * m_wb.direction; 1348 if (dy * m_wb.theta > 0.0) m_wb.width = -m_wb.width; 1349 } 1350 else 1351 { 1352 // Vertical strip 1353 if (is360) m_wb.y = firstY; 1354 else 1355 { 1356 if (lypos - fypos < 0) 1357 { 1358 m_wb.x = firstY + midY; 1359 z = ProjZ(last->trs, midX, 0.0, 1.0); 1360 prevX = ProjX(last->trs, midX, 0.0, z, 1.0); 1361 prevY = ProjY(last->trs, midX, 0.0, z, 1.0); 1362 } 1363 else 1364 { 1365 m_wb.x = firstX - midX; 1366 z = ProjZ(last->trs, midX, last->height - 1.0, 1.0); 1367 prevX = ProjX(last->trs, midX, last->height - 1.0, z, 1.0); 1368 prevY = ProjY(last->trs, midX, last->height - 1.0, z, 1.0); 1369 } 1370 } 1371 dx = prevX - firstX; 1372 if (dx < 0.0) m_wb.direction = 1.0; 1373 else m_wb.direction = -1.0; 1374 m_wb.x = firstX - m_wb.radius * m_wb.direction; 1375 if (dx * m_wb.theta > 0.0) m_wb.width = -m_wb.width; 1376 } 1377 1378 // Calculate the correct correction factor 1379 double deltaX = prevX - m_wb.x; 1380 double deltaY = prevY - m_wb.y; 1381 double length = sqrt(deltaX * deltaX + deltaY * deltaY); 1382 double deltaTheta = (m_wb.horizontal) ? deltaX : deltaY; 1383 deltaTheta = asin(deltaTheta / length); 1384 m_wb.correction = ((m_wb.radius - length) * m_wb.direction) / 1385 (deltaTheta / m_wb.theta); 1386 } 1387} 1388