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 32 Blend::Blend() 33 { 34 m_wb.blendingType = BLEND_TYPE_NONE; 35 } 36 37 Blend::~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 44 int 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 77 inline double max(double a, double b) { return a > b ? a : b; } 78 inline double min(double a, double b) { return a < b ? a : b; } 79 80 void 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 99 int 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 310 int 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 331 int 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 374 int 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_PXLS; 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_PXLS; 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 591 void 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 630 int 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 800 void 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 870 void 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 not on a seam OR pyramid level exceeds 945 // maximum level for cross-fading. 946 if((imgMos.V.ptr[jj][ii] == 128) || 947 (dscale > STRIP_CROSS_FADE_MAX_PYR_LEVEL)) 948 { 949 wt0 = 0.0; 950 wt1 = 1.0; 951 } 952 else 953 { 954 wt0 = 1.0; 955 wt1 = ((imgMos.Y.ptr[jj][ii] == site_idx) ? 956 (double)imgMos.U.ptr[jj][ii] / 100.0 : 957 1.0 - (double)imgMos.U.ptr[jj][ii] / 100.0); 958 } 959 } 960 } 961 962 // Project this mosaic point into the original frame coordinate space 963 double xx, yy; 964 965 MosaicToFrame(inv_trs, si, sj, xx, yy); 966 967 if (xx < 0.0 || yy < 0.0 || xx > width - 1.0 || yy > height - 1.0) 968 { 969 if(inMask) 970 { 971 imgMos.Y.ptr[jj][ii] = 255; 972 wt0 = 0.0f; 973 wt1 = 1.0f; 974 } 975 } 976 977 xx /= (1 << dscale); 978 yy /= (1 << dscale); 979 980 981 int x1 = (xx >= 0.0) ? (int) xx : (int) floor(xx); 982 int y1 = (yy >= 0.0) ? (int) yy : (int) floor(yy); 983 984 // Final destination in extended pyramid 985 #ifndef LINEAR_INTERP 986 if(inSegment(x1, sptr->width, BORDER-1) && 987 inSegment(y1, sptr->height, BORDER-1)) 988 { 989 double xfrac = xx - x1; 990 double yfrac = yy - y1; 991 dptr->ptr[j][i] = (short) (wt0 * dptr->ptr[j][i] + .5 + 992 wt1 * ciCalc(sptr, x1, y1, xfrac, yfrac)); 993 if (dvptr >= m_pMosaicVPyr && nC > 0) 994 { 995 duptr->ptr[j][i] = (short) (wt0 * duptr->ptr[j][i] + .5 + 996 wt1 * ciCalc(suptr, x1, y1, xfrac, yfrac)); 997 dvptr->ptr[j][i] = (short) (wt0 * dvptr->ptr[j][i] + .5 + 998 wt1 * ciCalc(svptr, x1, y1, xfrac, yfrac)); 999 } 1000 } 1001 #else 1002 if(inSegment(x1, sptr->width, BORDER) && inSegment(y1, sptr->height, BORDER)) 1003 { 1004 int x2 = x1 + 1; 1005 int y2 = y1 + 1; 1006 double xfrac = xx - x1; 1007 double yfrac = yy - y1; 1008 double y1val = sptr->ptr[y1][x1] + 1009 (sptr->ptr[y1][x2] - sptr->ptr[y1][x1]) * xfrac; 1010 double y2val = sptr->ptr[y2][x1] + 1011 (sptr->ptr[y2][x2] - sptr->ptr[y2][x1]) * xfrac; 1012 dptr->ptr[j][i] = (short) (y1val + yfrac * (y2val - y1val)); 1013 1014 if (dvptr >= m_pMosaicVPyr && nC > 0) 1015 { 1016 y1val = suptr->ptr[y1][x1] + 1017 (suptr->ptr[y1][x2] - suptr->ptr[y1][x1]) * xfrac; 1018 y2val = suptr->ptr[y2][x1] + 1019 (suptr->ptr[y2][x2] - suptr->ptr[y2][x1]) * xfrac; 1020 1021 duptr->ptr[j][i] = (short) (y1val + yfrac * (y2val - y1val)); 1022 1023 y1val = svptr->ptr[y1][x1] + 1024 (svptr->ptr[y1][x2] - svptr->ptr[y1][x1]) * xfrac; 1025 y2val = svptr->ptr[y2][x1] + 1026 (svptr->ptr[y2][x2] - svptr->ptr[y2][x1]) * xfrac; 1027 1028 dvptr->ptr[j][i] = (short) (y1val + yfrac * (y2val - y1val)); 1029 } 1030 } 1031 #endif 1032 else 1033 { 1034 clipToSegment(x1, sptr->width, BORDER); 1035 clipToSegment(y1, sptr->height, BORDER); 1036 1037 dptr->ptr[j][i] = (short) (wt0 * dptr->ptr[j][i] + 0.5 + 1038 wt1 * sptr->ptr[y1][x1] ); 1039 if (dvptr >= m_pMosaicVPyr && nC > 0) 1040 { 1041 dvptr->ptr[j][i] = (short) (wt0 * dvptr->ptr[j][i] + 1042 0.5 + wt1 * svptr->ptr[y1][x1] ); 1043 duptr->ptr[j][i] = (short) (wt0 * duptr->ptr[j][i] + 1044 0.5 + wt1 * suptr->ptr[y1][x1] ); 1045 } 1046 } 1047 } 1048 } 1049 } 1050 } 1051 1052 void Blend::MosaicToFrame(double trs[3][3], double x, double y, double &wx, double &wy) 1053 { 1054 double X, Y, z; 1055 if (m_wb.theta == 0.0) 1056 { 1057 X = x; 1058 Y = y; 1059 } 1060 else if (m_wb.horizontal) 1061 { 1062 double alpha = x * m_wb.direction / m_wb.width; 1063 double length = (y - alpha * m_wb.correction) * m_wb.direction + m_wb.radius; 1064 double deltaTheta = m_wb.theta * alpha; 1065 double sinTheta = sin(deltaTheta); 1066 double cosTheta = sqrt(1.0 - sinTheta * sinTheta) * m_wb.direction; 1067 X = length * sinTheta + m_wb.x; 1068 Y = length * cosTheta + m_wb.y; 1069 } 1070 else 1071 { 1072 double alpha = y * m_wb.direction / m_wb.width; 1073 double length = (x - alpha * m_wb.correction) * m_wb.direction + m_wb.radius; 1074 double deltaTheta = m_wb.theta * alpha; 1075 double sinTheta = sin(deltaTheta); 1076 double cosTheta = sqrt(1.0 - sinTheta * sinTheta) * m_wb.direction; 1077 Y = length * sinTheta + m_wb.y; 1078 X = length * cosTheta + m_wb.x; 1079 } 1080 z = ProjZ(trs, X, Y, 1.0); 1081 wx = ProjX(trs, X, Y, z, 1.0); 1082 wy = ProjY(trs, X, Y, z, 1.0); 1083 } 1084 1085 void Blend::FrameToMosaic(double trs[3][3], double x, double y, double &wx, double &wy) 1086 { 1087 // Project into the intermediate Mosaic coordinate system 1088 double z = ProjZ(trs, x, y, 1.0); 1089 double X = ProjX(trs, x, y, z, 1.0); 1090 double Y = ProjY(trs, x, y, z, 1.0); 1091 1092 if (m_wb.theta == 0.0) 1093 { 1094 // No rotation, then this is all we need to do. 1095 wx = X; 1096 wy = Y; 1097 } 1098 else if (m_wb.horizontal) 1099 { 1100 double deltaX = X - m_wb.x; 1101 double deltaY = Y - m_wb.y; 1102 double length = sqrt(deltaX * deltaX + deltaY * deltaY); 1103 double deltaTheta = asin(deltaX / length); 1104 double alpha = deltaTheta / m_wb.theta; 1105 wx = alpha * m_wb.width * m_wb.direction; 1106 wy = (length - m_wb.radius) * m_wb.direction + alpha * m_wb.correction; 1107 } 1108 else 1109 { 1110 double deltaX = X - m_wb.x; 1111 double deltaY = Y - m_wb.y; 1112 double length = sqrt(deltaX * deltaX + deltaY * deltaY); 1113 double deltaTheta = asin(deltaY / length); 1114 double alpha = deltaTheta / m_wb.theta; 1115 wy = alpha * m_wb.width * m_wb.direction; 1116 wx = (length - m_wb.radius) * m_wb.direction + alpha * m_wb.correction; 1117 } 1118 } 1119 1120 1121 1122 // Clip the region of interest as small as possible by using the Voronoi edges of 1123 // the neighbors 1124 void Blend::ClipBlendRect(CSite *csite, BlendRect &brect) 1125 { 1126 SEdgeVector *ce; 1127 int ecnt; 1128 for (ce = csite->getNeighbor(), ecnt = csite->getNumNeighbors(); ecnt--; ce++) 1129 { 1130 // calculate the Voronoi bisector intersection 1131 const double epsilon = 1e-5; 1132 double dx = (m_AllSites[ce->second].getVCenter().x - m_AllSites[ce->first].getVCenter().x); 1133 double dy = (m_AllSites[ce->second].getVCenter().y - m_AllSites[ce->first].getVCenter().y); 1134 double xmid = m_AllSites[ce->first].getVCenter().x + dx/2.0; 1135 double ymid = m_AllSites[ce->first].getVCenter().y + dy/2.0; 1136 double inter; 1137 1138 if (dx > epsilon) 1139 { 1140 // neighbor is on right 1141 if ((inter = m_wb.roundoffOverlap + xmid - dy * (((dy >= 0.0) ? brect.bot : brect.top) - ymid) / dx) < brect.rgt) 1142 brect.rgt = inter; 1143 } 1144 else if (dx < -epsilon) 1145 { 1146 // neighbor is on left 1147 if ((inter = -m_wb.roundoffOverlap + xmid - dy * (((dy >= 0.0) ? brect.bot : brect.top) - ymid) / dx) > brect.lft) 1148 brect.lft = inter; 1149 } 1150 if (dy > epsilon) 1151 { 1152 // neighbor is above 1153 if ((inter = m_wb.roundoffOverlap + ymid - dx * (((dx >= 0.0) ? brect.lft : brect.rgt) - xmid) / dy) < brect.top) 1154 brect.top = inter; 1155 } 1156 else if (dy < -epsilon) 1157 { 1158 // neighbor is below 1159 if ((inter = -m_wb.roundoffOverlap + ymid - dx * (((dx >= 0.0) ? brect.lft : brect.rgt) - xmid) / dy) > brect.bot) 1160 brect.bot = inter; 1161 } 1162 } 1163 } 1164 1165 void Blend::FrameToMosaicRect(int width, int height, double trs[3][3], BlendRect &brect) 1166 { 1167 // We need to walk the perimeter since the borders can be bent. 1168 brect.lft = brect.bot = 2e30; 1169 brect.rgt = brect.top = -2e30; 1170 double xpos, ypos; 1171 double lasty = height - 1.0; 1172 double lastx = width - 1.0; 1173 int i; 1174 1175 for (i = width; i--;) 1176 { 1177 1178 FrameToMosaic(trs, (double) i, 0.0, xpos, ypos); 1179 ClipRect(xpos, ypos, brect); 1180 FrameToMosaic(trs, (double) i, lasty, xpos, ypos); 1181 ClipRect(xpos, ypos, brect); 1182 } 1183 for (i = height; i--;) 1184 { 1185 FrameToMosaic(trs, 0.0, (double) i, xpos, ypos); 1186 ClipRect(xpos, ypos, brect); 1187 FrameToMosaic(trs, lastx, (double) i, xpos, ypos); 1188 ClipRect(xpos, ypos, brect); 1189 } 1190 } 1191 1192 void Blend::SelectRelevantFrames(MosaicFrame **frames, int frames_size, 1193 MosaicFrame **relevant_frames, int &relevant_frames_size) 1194 { 1195 MosaicFrame *first = frames[0]; 1196 MosaicFrame *last = frames[frames_size-1]; 1197 MosaicFrame *mb; 1198 1199 double fxpos = first->trs[0][2], fypos = first->trs[1][2]; 1200 1201 double midX = last->width / 2.0; 1202 double midY = last->height / 2.0; 1203 double z = ProjZ(first->trs, midX, midY, 1.0); 1204 double firstX, firstY; 1205 double prevX = firstX = ProjX(first->trs, midX, midY, z, 1.0); 1206 double prevY = firstY = ProjY(first->trs, midX, midY, z, 1.0); 1207 1208 relevant_frames[0] = first; // Add first frame by default 1209 relevant_frames_size = 1; 1210 1211 for (int i = 0; i < frames_size - 1; i++) 1212 { 1213 mb = frames[i]; 1214 double currX, currY; 1215 z = ProjZ(mb->trs, midX, midY, 1.0); 1216 currX = ProjX(mb->trs, midX, midY, z, 1.0); 1217 currY = ProjY(mb->trs, midX, midY, z, 1.0); 1218 double deltaX = currX - prevX; 1219 double deltaY = currY - prevY; 1220 double center2centerDist = sqrt(deltaY * deltaY + deltaX * deltaX); 1221 1222 if (fabs(deltaX) > STRIP_SEPARATION_THRESHOLD_PXLS || 1223 fabs(deltaY) > STRIP_SEPARATION_THRESHOLD_PXLS) 1224 { 1225 relevant_frames[relevant_frames_size] = mb; 1226 relevant_frames_size++; 1227 1228 prevX = currX; 1229 prevY = currY; 1230 } 1231 } 1232 1233 // Add last frame by default 1234 relevant_frames[relevant_frames_size] = last; 1235 relevant_frames_size++; 1236 } 1237 1238 void Blend::ComputeBlendParameters(MosaicFrame **frames, int frames_size, int is360) 1239 { 1240 // For FULL and PAN modes, we do not unwarp the mosaic into a rectangular coordinate system 1241 // and so we set the theta to 0 and return. 1242 if (m_wb.blendingType != BLEND_TYPE_CYLPAN && m_wb.blendingType != BLEND_TYPE_HORZ) 1243 { 1244 m_wb.theta = 0.0; 1245 return; 1246 } 1247 1248 MosaicFrame *first = frames[0]; 1249 MosaicFrame *last = frames[frames_size-1]; 1250 MosaicFrame *mb; 1251 1252 double lxpos = last->trs[0][2], lypos = last->trs[1][2]; 1253 double fxpos = first->trs[0][2], fypos = first->trs[1][2]; 1254 1255 // Calculate warp to produce proper stitching. 1256 // get x, y displacement 1257 double midX = last->width / 2.0; 1258 double midY = last->height / 2.0; 1259 double z = ProjZ(first->trs, midX, midY, 1.0); 1260 double firstX, firstY; 1261 double prevX = firstX = ProjX(first->trs, midX, midY, z, 1.0); 1262 double prevY = firstY = ProjY(first->trs, midX, midY, z, 1.0); 1263 1264 double arcLength, lastTheta; 1265 m_wb.theta = lastTheta = arcLength = 0.0; 1266 1267 // Step through all the frames to compute the total arc-length of the cone 1268 // swept while capturing the mosaic (in the original conical coordinate system). 1269 for (int i = 0; i < frames_size; i++) 1270 { 1271 mb = frames[i]; 1272 double currX, currY; 1273 z = ProjZ(mb->trs, midX, midY, 1.0); 1274 currX = ProjX(mb->trs, midX, midY, z, 1.0); 1275 currY = ProjY(mb->trs, midX, midY, z, 1.0); 1276 double deltaX = currX - prevX; 1277 double deltaY = currY - prevY; 1278 1279 // The arcLength is computed by summing the lengths of the chords 1280 // connecting the pairwise projected image centers of the input image frames. 1281 arcLength += sqrt(deltaY * deltaY + deltaX * deltaX); 1282 1283 if (!is360) 1284 { 1285 double thisTheta = asin(mb->trs[1][0]); 1286 m_wb.theta += thisTheta - lastTheta; 1287 lastTheta = thisTheta; 1288 } 1289 1290 prevX = currX; 1291 prevY = currY; 1292 } 1293 1294 // Stretch this to end at the proper alignment i.e. the width of the 1295 // rectangle is determined by the arcLength computed above and the cone 1296 // sector angle is determined using the rotation of the last frame. 1297 m_wb.width = arcLength; 1298 if (is360) m_wb.theta = asin(last->trs[1][0]); 1299 1300 // If there is no rotation, we're done. 1301 if (m_wb.theta != 0.0) 1302 { 1303 double dx = prevX - firstX; 1304 double dy = prevY - firstY; 1305 1306 // If the mosaic was captured by sweeping horizontally 1307 if (abs(lxpos - fxpos) > abs(lypos - fypos)) 1308 { 1309 m_wb.horizontal = 1; 1310 // Calculate radius position to make ends exactly the same Y offset 1311 double radiusTheta = dx / cos(3.14159 / 2.0 - m_wb.theta); 1312 m_wb.radius = dy + radiusTheta * cos(m_wb.theta); 1313 if (m_wb.radius < 0.0) m_wb.radius = -m_wb.radius; 1314 } 1315 else 1316 { 1317 m_wb.horizontal = 0; 1318 // Calculate radius position to make ends exactly the same Y offset 1319 double radiusTheta = dy / cos(3.14159 / 2.0 - m_wb.theta); 1320 m_wb.radius = dx + radiusTheta * cos(m_wb.theta); 1321 if (m_wb.radius < 0.0) m_wb.radius = -m_wb.radius; 1322 } 1323 1324 // Determine major direction 1325 if (m_wb.horizontal) 1326 { 1327 // Horizontal strip 1328 // m_wb.x,y record the origin of the rectangle coordinate system. 1329 if (is360) m_wb.x = firstX; 1330 else 1331 { 1332 if (lxpos - fxpos < 0) 1333 { 1334 m_wb.x = firstX + midX; 1335 z = ProjZ(last->trs, 0.0, midY, 1.0); 1336 prevX = ProjX(last->trs, 0.0, midY, z, 1.0); 1337 prevY = ProjY(last->trs, 0.0, midY, z, 1.0); 1338 } 1339 else 1340 { 1341 m_wb.x = firstX - midX; 1342 z = ProjZ(last->trs, last->width - 1.0, midY, 1.0); 1343 prevX = ProjX(last->trs, last->width - 1.0, midY, z, 1.0); 1344 prevY = ProjY(last->trs, last->width - 1.0, midY, z, 1.0); 1345 } 1346 } 1347 dy = prevY - firstY; 1348 if (dy < 0.0) m_wb.direction = 1.0; 1349 else m_wb.direction = -1.0; 1350 m_wb.y = firstY - m_wb.radius * m_wb.direction; 1351 if (dy * m_wb.theta > 0.0) m_wb.width = -m_wb.width; 1352 } 1353 else 1354 { 1355 // Vertical strip 1356 if (is360) m_wb.y = firstY; 1357 else 1358 { 1359 if (lypos - fypos < 0) 1360 { 1361 m_wb.x = firstY + midY; 1362 z = ProjZ(last->trs, midX, 0.0, 1.0); 1363 prevX = ProjX(last->trs, midX, 0.0, z, 1.0); 1364 prevY = ProjY(last->trs, midX, 0.0, z, 1.0); 1365 } 1366 else 1367 { 1368 m_wb.x = firstX - midX; 1369 z = ProjZ(last->trs, midX, last->height - 1.0, 1.0); 1370 prevX = ProjX(last->trs, midX, last->height - 1.0, z, 1.0); 1371 prevY = ProjY(last->trs, midX, last->height - 1.0, z, 1.0); 1372 } 1373 } 1374 dx = prevX - firstX; 1375 if (dx < 0.0) m_wb.direction = 1.0; 1376 else m_wb.direction = -1.0; 1377 m_wb.x = firstX - m_wb.radius * m_wb.direction; 1378 if (dx * m_wb.theta > 0.0) m_wb.width = -m_wb.width; 1379 } 1380 1381 // Calculate the correct correction factor 1382 double deltaX = prevX - m_wb.x; 1383 double deltaY = prevY - m_wb.y; 1384 double length = sqrt(deltaX * deltaX + deltaY * deltaY); 1385 double deltaTheta = (m_wb.horizontal) ? deltaX : deltaY; 1386 deltaTheta = asin(deltaTheta / length); 1387 m_wb.correction = ((m_wb.radius - length) * m_wb.direction) / 1388 (deltaTheta / m_wb.theta); 1389 } 1390 } 1391