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
Blend()32 Blend::Blend()
33 {
34 m_wb.blendingType = BLEND_TYPE_NONE;
35 }
36
~Blend()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
initialize(int blendingType,int stripType,int frame_width,int frame_height)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
max(double a,double b)77 inline double max(double a, double b) { return a > b ? a : b; }
min(double a,double b)78 inline double min(double a, double b) { return a < b ? a : b; }
79
AlignToMiddleFrame(MosaicFrame ** frames,int frames_size)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
runBlend(MosaicFrame ** oframes,MosaicFrame ** rframes,int frames_size,ImageType & imageMosaicYVU,int & mosaicWidth,int & mosaicHeight,float & progress,bool & cancelComputation)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
MosaicSizeCheck(float sizeMultiplier,float heightMultiplier)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
FillFramePyramid(MosaicFrame * mb)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
DoMergeAndBlend(MosaicFrame ** frames,int nsite,int width,int height,YUVinfo & imgMos,MosaicRect & rect,MosaicRect & cropping_rect,float & progress,bool & cancelComputation)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 (cropping_rect.Width() <= 0 || cropping_rect.Height() <= 0)
583 {
584 LOGE("Size of the cropping_rect is invalid - (width, height): (%d, %d)",
585 cropping_rect.Width(), cropping_rect.Height());
586 return BLEND_RET_ERROR;
587 }
588
589 if (m_pMosaicVPyr) free(m_pMosaicVPyr);
590 if (m_pMosaicUPyr) free(m_pMosaicUPyr);
591 if (m_pMosaicYPyr) free(m_pMosaicYPyr);
592
593 progress += TIME_PERCENT_FINAL;
594
595 return BLEND_RET_OK;
596 }
597
CropFinalMosaic(YUVinfo & imgMos,MosaicRect & cropping_rect)598 void Blend::CropFinalMosaic(YUVinfo &imgMos, MosaicRect &cropping_rect)
599 {
600 int i, j, k;
601 ImageType yimg;
602 ImageType uimg;
603 ImageType vimg;
604
605
606 yimg = imgMos.Y.ptr[0];
607 uimg = imgMos.U.ptr[0];
608 vimg = imgMos.V.ptr[0];
609
610 k = 0;
611 for (j = cropping_rect.top; j <= cropping_rect.bottom; j++)
612 {
613 for (i = cropping_rect.left; i <= cropping_rect.right; i++)
614 {
615 yimg[k] = yimg[j*imgMos.Y.width+i];
616 k++;
617 }
618 }
619 for (j = cropping_rect.top; j <= cropping_rect.bottom; j++)
620 {
621 for (i = cropping_rect.left; i <= cropping_rect.right; i++)
622 {
623 yimg[k] = vimg[j*imgMos.Y.width+i];
624 k++;
625 }
626 }
627 for (j = cropping_rect.top; j <= cropping_rect.bottom; j++)
628 {
629 for (i = cropping_rect.left; i <= cropping_rect.right; i++)
630 {
631 yimg[k] = uimg[j*imgMos.Y.width+i];
632 k++;
633 }
634 }
635 }
636
PerformFinalBlending(YUVinfo & imgMos,MosaicRect & cropping_rect)637 int Blend::PerformFinalBlending(YUVinfo &imgMos, MosaicRect &cropping_rect)
638 {
639 if (!PyramidShort::BorderExpand(m_pMosaicYPyr, m_wb.nlevs, 1) || !PyramidShort::BorderExpand(m_pMosaicUPyr, m_wb.nlevsC, 1) ||
640 !PyramidShort::BorderExpand(m_pMosaicVPyr, m_wb.nlevsC, 1))
641 {
642 LOGE("Error: Could not BorderExpand!");
643 return BLEND_RET_ERROR;
644 }
645
646 ImageTypeShort myimg;
647 ImageTypeShort muimg;
648 ImageTypeShort mvimg;
649 ImageType yimg;
650 ImageType uimg;
651 ImageType vimg;
652
653 int cx = (int)imgMos.Y.width/2;
654 int cy = (int)imgMos.Y.height/2;
655
656 // 2D boolean array that contains true wherever the mosaic image data is
657 // invalid (i.e. in the gray border).
658 bool **b = new bool*[imgMos.Y.height];
659
660 for(int j=0; j<imgMos.Y.height; j++)
661 {
662 b[j] = new bool[imgMos.Y.width];
663 }
664
665 // Copy the resulting image into the full image using the mask
666 int i, j;
667
668 yimg = imgMos.Y.ptr[0];
669 uimg = imgMos.U.ptr[0];
670 vimg = imgMos.V.ptr[0];
671
672 for (j = 0; j < imgMos.Y.height; j++)
673 {
674 myimg = m_pMosaicYPyr->ptr[j];
675 muimg = m_pMosaicUPyr->ptr[j];
676 mvimg = m_pMosaicVPyr->ptr[j];
677
678 for (i = 0; i<imgMos.Y.width; i++)
679 {
680 // A final mask was set up previously,
681 // if the value is zero skip it, otherwise replace it.
682 if (*yimg <255)
683 {
684 short value = (short) ((*myimg) >> 3);
685 if (value < 0) value = 0;
686 else if (value > 255) value = 255;
687 *yimg = (unsigned char) value;
688
689 value = (short) ((*muimg) >> 3);
690 if (value < 0) value = 0;
691 else if (value > 255) value = 255;
692 *uimg = (unsigned char) value;
693
694 value = (short) ((*mvimg) >> 3);
695 if (value < 0) value = 0;
696 else if (value > 255) value = 255;
697 *vimg = (unsigned char) value;
698
699 b[j][i] = false;
700
701 }
702 else
703 { // set border color in here
704 *yimg = (unsigned char) 96;
705 *uimg = (unsigned char) 128;
706 *vimg = (unsigned char) 128;
707
708 b[j][i] = true;
709 }
710
711 yimg++;
712 uimg++;
713 vimg++;
714 myimg++;
715 muimg++;
716 mvimg++;
717 }
718 }
719
720 if(m_wb.horizontal)
721 {
722 //Scan through each row and increment top if the row contains any gray
723 for (j = 0; j < imgMos.Y.height; j++)
724 {
725 for (i = cropping_rect.left; i < cropping_rect.right; i++)
726 {
727 if (b[j][i])
728 {
729 break; // to next row
730 }
731 }
732
733 if (i == cropping_rect.right) //no gray pixel in this row!
734 {
735 cropping_rect.top = j;
736 break;
737 }
738 }
739
740 //Scan through each row and decrement bottom if the row contains any gray
741 for (j = imgMos.Y.height-1; j >= 0; j--)
742 {
743 for (i = cropping_rect.left; i < cropping_rect.right; i++)
744 {
745 if (b[j][i])
746 {
747 break; // to next row
748 }
749 }
750
751 if (i == cropping_rect.right) //no gray pixel in this row!
752 {
753 cropping_rect.bottom = j;
754 break;
755 }
756 }
757 }
758 else // Vertical Mosaic
759 {
760 //Scan through each column and increment left if the column contains any gray
761 for (i = 0; i < imgMos.Y.width; i++)
762 {
763 for (j = cropping_rect.top; j < cropping_rect.bottom; j++)
764 {
765 if (b[j][i])
766 {
767 break; // to next column
768 }
769 }
770
771 if (j == cropping_rect.bottom) //no gray pixel in this column!
772 {
773 cropping_rect.left = i;
774 break;
775 }
776 }
777
778 //Scan through each column and decrement right if the column contains any gray
779 for (i = imgMos.Y.width-1; i >= 0; i--)
780 {
781 for (j = cropping_rect.top; j < cropping_rect.bottom; j++)
782 {
783 if (b[j][i])
784 {
785 break; // to next column
786 }
787 }
788
789 if (j == cropping_rect.bottom) //no gray pixel in this column!
790 {
791 cropping_rect.right = i;
792 break;
793 }
794 }
795
796 }
797
798 RoundingCroppingSizeToMultipleOf8(cropping_rect);
799
800 for(int j=0; j<imgMos.Y.height; j++)
801 {
802 delete b[j];
803 }
804
805 delete b;
806
807 return BLEND_RET_OK;
808 }
809
RoundingCroppingSizeToMultipleOf8(MosaicRect & rect)810 void Blend::RoundingCroppingSizeToMultipleOf8(MosaicRect &rect) {
811 int height = rect.bottom - rect.top + 1;
812 int residue = height & 7;
813 rect.bottom -= residue;
814
815 int width = rect.right - rect.left + 1;
816 residue = width & 7;
817 rect.right -= residue;
818 }
819
ComputeMask(CSite * csite,BlendRect & vcrect,BlendRect & brect,MosaicRect & rect,YUVinfo & imgMos,int site_idx)820 void Blend::ComputeMask(CSite *csite, BlendRect &vcrect, BlendRect &brect, MosaicRect &rect, YUVinfo &imgMos, int site_idx)
821 {
822 PyramidShort *dptr = m_pMosaicYPyr;
823
824 int nC = m_wb.nlevsC;
825 int l = (int) ((vcrect.lft - rect.left));
826 int b = (int) ((vcrect.bot - rect.top));
827 int r = (int) ((vcrect.rgt - rect.left));
828 int t = (int) ((vcrect.top - rect.top));
829
830 if (vcrect.lft == brect.lft)
831 l = (l <= 0) ? -BORDER : l - BORDER;
832 else if (l < -BORDER)
833 l = -BORDER;
834
835 if (vcrect.bot == brect.bot)
836 b = (b <= 0) ? -BORDER : b - BORDER;
837 else if (b < -BORDER)
838 b = -BORDER;
839
840 if (vcrect.rgt == brect.rgt)
841 r = (r >= dptr->width) ? dptr->width + BORDER - 1 : r + BORDER;
842 else if (r >= dptr->width + BORDER)
843 r = dptr->width + BORDER - 1;
844
845 if (vcrect.top == brect.top)
846 t = (t >= dptr->height) ? dptr->height + BORDER - 1 : t + BORDER;
847 else if (t >= dptr->height + BORDER)
848 t = dptr->height + BORDER - 1;
849
850 // Walk the Region of interest and populate the pyramid
851 for (int j = b; j <= t; j++)
852 {
853 int jj = j;
854 double sj = jj + rect.top;
855
856 for (int i = l; i <= r; i++)
857 {
858 int ii = i;
859 // project point and then triangulate to neighbors
860 double si = ii + rect.left;
861
862 double dself = hypotSq(csite->getVCenter().x - si, csite->getVCenter().y - sj);
863 int inMask = ((unsigned) ii < imgMos.Y.width &&
864 (unsigned) jj < imgMos.Y.height) ? 1 : 0;
865
866 if(!inMask)
867 continue;
868
869 // scan the neighbors to see if this is a valid position
870 unsigned char mask = (unsigned char) 255;
871 SEdgeVector *ce;
872 int ecnt;
873 for (ce = csite->getNeighbor(), ecnt = csite->getNumNeighbors(); ecnt--; ce++)
874 {
875 double d1 = hypotSq(m_AllSites[ce->second].getVCenter().x - si,
876 m_AllSites[ce->second].getVCenter().y - sj);
877 if (d1 < dself)
878 {
879 break;
880 }
881 }
882
883 if (ecnt >= 0) continue;
884
885 imgMos.Y.ptr[jj][ii] = (unsigned char)site_idx;
886 }
887 }
888 }
889
ProcessPyramidForThisFrame(CSite * csite,BlendRect & vcrect,BlendRect & brect,MosaicRect & rect,YUVinfo & imgMos,double trs[3][3],int site_idx)890 void Blend::ProcessPyramidForThisFrame(CSite *csite, BlendRect &vcrect, BlendRect &brect, MosaicRect &rect, YUVinfo &imgMos, double trs[3][3], int site_idx)
891 {
892 // Put the Region of interest (for all levels) into m_pMosaicYPyr
893 double inv_trs[3][3];
894 inv33d(trs, inv_trs);
895
896 // Process each pyramid level
897 PyramidShort *sptr = m_pFrameYPyr;
898 PyramidShort *suptr = m_pFrameUPyr;
899 PyramidShort *svptr = m_pFrameVPyr;
900
901 PyramidShort *dptr = m_pMosaicYPyr;
902 PyramidShort *duptr = m_pMosaicUPyr;
903 PyramidShort *dvptr = m_pMosaicVPyr;
904
905 int dscale = 0; // distance scale for the current level
906 int nC = m_wb.nlevsC;
907 for (int n = m_wb.nlevs; n--; dscale++, dptr++, sptr++, dvptr++, duptr++, svptr++, suptr++, nC--)
908 {
909 int l = (int) ((vcrect.lft - rect.left) / (1 << dscale));
910 int b = (int) ((vcrect.bot - rect.top) / (1 << dscale));
911 int r = (int) ((vcrect.rgt - rect.left) / (1 << dscale) + .5);
912 int t = (int) ((vcrect.top - rect.top) / (1 << dscale) + .5);
913
914 if (vcrect.lft == brect.lft)
915 l = (l <= 0) ? -BORDER : l - BORDER;
916 else if (l < -BORDER)
917 l = -BORDER;
918
919 if (vcrect.bot == brect.bot)
920 b = (b <= 0) ? -BORDER : b - BORDER;
921 else if (b < -BORDER)
922 b = -BORDER;
923
924 if (vcrect.rgt == brect.rgt)
925 r = (r >= dptr->width) ? dptr->width + BORDER - 1 : r + BORDER;
926 else if (r >= dptr->width + BORDER)
927 r = dptr->width + BORDER - 1;
928
929 if (vcrect.top == brect.top)
930 t = (t >= dptr->height) ? dptr->height + BORDER - 1 : t + BORDER;
931 else if (t >= dptr->height + BORDER)
932 t = dptr->height + BORDER - 1;
933
934 // Walk the Region of interest and populate the pyramid
935 for (int j = b; j <= t; j++)
936 {
937 int jj = (j << dscale);
938 double sj = jj + rect.top;
939
940 for (int i = l; i <= r; i++)
941 {
942 int ii = (i << dscale);
943 // project point and then triangulate to neighbors
944 double si = ii + rect.left;
945
946 int inMask = ((unsigned) ii < imgMos.Y.width &&
947 (unsigned) jj < imgMos.Y.height) ? 1 : 0;
948
949 if(inMask && imgMos.Y.ptr[jj][ii] != site_idx &&
950 imgMos.V.ptr[jj][ii] != site_idx &&
951 imgMos.Y.ptr[jj][ii] != 255)
952 continue;
953
954 // Setup weights for cross-fading
955 // Weight of the intensity already in the output pixel
956 double wt0 = 0.0;
957 // Weight of the intensity from the input pixel (current frame)
958 double wt1 = 1.0;
959
960 if (m_wb.stripType == STRIP_TYPE_WIDE)
961 {
962 if(inMask && imgMos.Y.ptr[jj][ii] != 255)
963 {
964 // If not on a seam OR pyramid level exceeds
965 // maximum level for cross-fading.
966 if((imgMos.V.ptr[jj][ii] == 128) ||
967 (dscale > STRIP_CROSS_FADE_MAX_PYR_LEVEL))
968 {
969 wt0 = 0.0;
970 wt1 = 1.0;
971 }
972 else
973 {
974 wt0 = 1.0;
975 wt1 = ((imgMos.Y.ptr[jj][ii] == site_idx) ?
976 (double)imgMos.U.ptr[jj][ii] / 100.0 :
977 1.0 - (double)imgMos.U.ptr[jj][ii] / 100.0);
978 }
979 }
980 }
981
982 // Project this mosaic point into the original frame coordinate space
983 double xx, yy;
984
985 MosaicToFrame(inv_trs, si, sj, xx, yy);
986
987 if (xx < 0.0 || yy < 0.0 || xx > width - 1.0 || yy > height - 1.0)
988 {
989 if(inMask)
990 {
991 imgMos.Y.ptr[jj][ii] = 255;
992 wt0 = 0.0f;
993 wt1 = 1.0f;
994 }
995 }
996
997 xx /= (1 << dscale);
998 yy /= (1 << dscale);
999
1000
1001 int x1 = (xx >= 0.0) ? (int) xx : (int) floor(xx);
1002 int y1 = (yy >= 0.0) ? (int) yy : (int) floor(yy);
1003
1004 // Final destination in extended pyramid
1005 #ifndef LINEAR_INTERP
1006 if(inSegment(x1, sptr->width, BORDER-1) &&
1007 inSegment(y1, sptr->height, BORDER-1))
1008 {
1009 double xfrac = xx - x1;
1010 double yfrac = yy - y1;
1011 dptr->ptr[j][i] = (short) (wt0 * dptr->ptr[j][i] + .5 +
1012 wt1 * ciCalc(sptr, x1, y1, xfrac, yfrac));
1013 if (dvptr >= m_pMosaicVPyr && nC > 0)
1014 {
1015 duptr->ptr[j][i] = (short) (wt0 * duptr->ptr[j][i] + .5 +
1016 wt1 * ciCalc(suptr, x1, y1, xfrac, yfrac));
1017 dvptr->ptr[j][i] = (short) (wt0 * dvptr->ptr[j][i] + .5 +
1018 wt1 * ciCalc(svptr, x1, y1, xfrac, yfrac));
1019 }
1020 }
1021 #else
1022 if(inSegment(x1, sptr->width, BORDER) && inSegment(y1, sptr->height, BORDER))
1023 {
1024 int x2 = x1 + 1;
1025 int y2 = y1 + 1;
1026 double xfrac = xx - x1;
1027 double yfrac = yy - y1;
1028 double y1val = sptr->ptr[y1][x1] +
1029 (sptr->ptr[y1][x2] - sptr->ptr[y1][x1]) * xfrac;
1030 double y2val = sptr->ptr[y2][x1] +
1031 (sptr->ptr[y2][x2] - sptr->ptr[y2][x1]) * xfrac;
1032 dptr->ptr[j][i] = (short) (y1val + yfrac * (y2val - y1val));
1033
1034 if (dvptr >= m_pMosaicVPyr && nC > 0)
1035 {
1036 y1val = suptr->ptr[y1][x1] +
1037 (suptr->ptr[y1][x2] - suptr->ptr[y1][x1]) * xfrac;
1038 y2val = suptr->ptr[y2][x1] +
1039 (suptr->ptr[y2][x2] - suptr->ptr[y2][x1]) * xfrac;
1040
1041 duptr->ptr[j][i] = (short) (y1val + yfrac * (y2val - y1val));
1042
1043 y1val = svptr->ptr[y1][x1] +
1044 (svptr->ptr[y1][x2] - svptr->ptr[y1][x1]) * xfrac;
1045 y2val = svptr->ptr[y2][x1] +
1046 (svptr->ptr[y2][x2] - svptr->ptr[y2][x1]) * xfrac;
1047
1048 dvptr->ptr[j][i] = (short) (y1val + yfrac * (y2val - y1val));
1049 }
1050 }
1051 #endif
1052 else
1053 {
1054 clipToSegment(x1, sptr->width, BORDER);
1055 clipToSegment(y1, sptr->height, BORDER);
1056
1057 dptr->ptr[j][i] = (short) (wt0 * dptr->ptr[j][i] + 0.5 +
1058 wt1 * sptr->ptr[y1][x1] );
1059 if (dvptr >= m_pMosaicVPyr && nC > 0)
1060 {
1061 dvptr->ptr[j][i] = (short) (wt0 * dvptr->ptr[j][i] +
1062 0.5 + wt1 * svptr->ptr[y1][x1] );
1063 duptr->ptr[j][i] = (short) (wt0 * duptr->ptr[j][i] +
1064 0.5 + wt1 * suptr->ptr[y1][x1] );
1065 }
1066 }
1067 }
1068 }
1069 }
1070 }
1071
MosaicToFrame(double trs[3][3],double x,double y,double & wx,double & wy)1072 void Blend::MosaicToFrame(double trs[3][3], double x, double y, double &wx, double &wy)
1073 {
1074 double X, Y, z;
1075 if (m_wb.theta == 0.0)
1076 {
1077 X = x;
1078 Y = y;
1079 }
1080 else if (m_wb.horizontal)
1081 {
1082 double alpha = x * m_wb.direction / m_wb.width;
1083 double length = (y - alpha * m_wb.correction) * m_wb.direction + m_wb.radius;
1084 double deltaTheta = m_wb.theta * alpha;
1085 double sinTheta = sin(deltaTheta);
1086 double cosTheta = sqrt(1.0 - sinTheta * sinTheta) * m_wb.direction;
1087 X = length * sinTheta + m_wb.x;
1088 Y = length * cosTheta + m_wb.y;
1089 }
1090 else
1091 {
1092 double alpha = y * m_wb.direction / m_wb.width;
1093 double length = (x - alpha * m_wb.correction) * m_wb.direction + m_wb.radius;
1094 double deltaTheta = m_wb.theta * alpha;
1095 double sinTheta = sin(deltaTheta);
1096 double cosTheta = sqrt(1.0 - sinTheta * sinTheta) * m_wb.direction;
1097 Y = length * sinTheta + m_wb.y;
1098 X = length * cosTheta + m_wb.x;
1099 }
1100 z = ProjZ(trs, X, Y, 1.0);
1101 wx = ProjX(trs, X, Y, z, 1.0);
1102 wy = ProjY(trs, X, Y, z, 1.0);
1103 }
1104
FrameToMosaic(double trs[3][3],double x,double y,double & wx,double & wy)1105 void Blend::FrameToMosaic(double trs[3][3], double x, double y, double &wx, double &wy)
1106 {
1107 // Project into the intermediate Mosaic coordinate system
1108 double z = ProjZ(trs, x, y, 1.0);
1109 double X = ProjX(trs, x, y, z, 1.0);
1110 double Y = ProjY(trs, x, y, z, 1.0);
1111
1112 if (m_wb.theta == 0.0)
1113 {
1114 // No rotation, then this is all we need to do.
1115 wx = X;
1116 wy = Y;
1117 }
1118 else if (m_wb.horizontal)
1119 {
1120 double deltaX = X - m_wb.x;
1121 double deltaY = Y - m_wb.y;
1122 double length = sqrt(deltaX * deltaX + deltaY * deltaY);
1123 double deltaTheta = asin(deltaX / length);
1124 double alpha = deltaTheta / m_wb.theta;
1125 wx = alpha * m_wb.width * m_wb.direction;
1126 wy = (length - m_wb.radius) * m_wb.direction + alpha * m_wb.correction;
1127 }
1128 else
1129 {
1130 double deltaX = X - m_wb.x;
1131 double deltaY = Y - m_wb.y;
1132 double length = sqrt(deltaX * deltaX + deltaY * deltaY);
1133 double deltaTheta = asin(deltaY / length);
1134 double alpha = deltaTheta / m_wb.theta;
1135 wy = alpha * m_wb.width * m_wb.direction;
1136 wx = (length - m_wb.radius) * m_wb.direction + alpha * m_wb.correction;
1137 }
1138 }
1139
1140
1141
1142 // Clip the region of interest as small as possible by using the Voronoi edges of
1143 // the neighbors
ClipBlendRect(CSite * csite,BlendRect & brect)1144 void Blend::ClipBlendRect(CSite *csite, BlendRect &brect)
1145 {
1146 SEdgeVector *ce;
1147 int ecnt;
1148 for (ce = csite->getNeighbor(), ecnt = csite->getNumNeighbors(); ecnt--; ce++)
1149 {
1150 // calculate the Voronoi bisector intersection
1151 const double epsilon = 1e-5;
1152 double dx = (m_AllSites[ce->second].getVCenter().x - m_AllSites[ce->first].getVCenter().x);
1153 double dy = (m_AllSites[ce->second].getVCenter().y - m_AllSites[ce->first].getVCenter().y);
1154 double xmid = m_AllSites[ce->first].getVCenter().x + dx/2.0;
1155 double ymid = m_AllSites[ce->first].getVCenter().y + dy/2.0;
1156 double inter;
1157
1158 if (dx > epsilon)
1159 {
1160 // neighbor is on right
1161 if ((inter = m_wb.roundoffOverlap + xmid - dy * (((dy >= 0.0) ? brect.bot : brect.top) - ymid) / dx) < brect.rgt)
1162 brect.rgt = inter;
1163 }
1164 else if (dx < -epsilon)
1165 {
1166 // neighbor is on left
1167 if ((inter = -m_wb.roundoffOverlap + xmid - dy * (((dy >= 0.0) ? brect.bot : brect.top) - ymid) / dx) > brect.lft)
1168 brect.lft = inter;
1169 }
1170 if (dy > epsilon)
1171 {
1172 // neighbor is above
1173 if ((inter = m_wb.roundoffOverlap + ymid - dx * (((dx >= 0.0) ? brect.lft : brect.rgt) - xmid) / dy) < brect.top)
1174 brect.top = inter;
1175 }
1176 else if (dy < -epsilon)
1177 {
1178 // neighbor is below
1179 if ((inter = -m_wb.roundoffOverlap + ymid - dx * (((dx >= 0.0) ? brect.lft : brect.rgt) - xmid) / dy) > brect.bot)
1180 brect.bot = inter;
1181 }
1182 }
1183 }
1184
FrameToMosaicRect(int width,int height,double trs[3][3],BlendRect & brect)1185 void Blend::FrameToMosaicRect(int width, int height, double trs[3][3], BlendRect &brect)
1186 {
1187 // We need to walk the perimeter since the borders can be bent.
1188 brect.lft = brect.bot = 2e30;
1189 brect.rgt = brect.top = -2e30;
1190 double xpos, ypos;
1191 double lasty = height - 1.0;
1192 double lastx = width - 1.0;
1193 int i;
1194
1195 for (i = width; i--;)
1196 {
1197
1198 FrameToMosaic(trs, (double) i, 0.0, xpos, ypos);
1199 ClipRect(xpos, ypos, brect);
1200 FrameToMosaic(trs, (double) i, lasty, xpos, ypos);
1201 ClipRect(xpos, ypos, brect);
1202 }
1203 for (i = height; i--;)
1204 {
1205 FrameToMosaic(trs, 0.0, (double) i, xpos, ypos);
1206 ClipRect(xpos, ypos, brect);
1207 FrameToMosaic(trs, lastx, (double) i, xpos, ypos);
1208 ClipRect(xpos, ypos, brect);
1209 }
1210 }
1211
SelectRelevantFrames(MosaicFrame ** frames,int frames_size,MosaicFrame ** relevant_frames,int & relevant_frames_size)1212 void Blend::SelectRelevantFrames(MosaicFrame **frames, int frames_size,
1213 MosaicFrame **relevant_frames, int &relevant_frames_size)
1214 {
1215 MosaicFrame *first = frames[0];
1216 MosaicFrame *last = frames[frames_size-1];
1217 MosaicFrame *mb;
1218
1219 double fxpos = first->trs[0][2], fypos = first->trs[1][2];
1220
1221 double midX = last->width / 2.0;
1222 double midY = last->height / 2.0;
1223 double z = ProjZ(first->trs, midX, midY, 1.0);
1224 double firstX, firstY;
1225 double prevX = firstX = ProjX(first->trs, midX, midY, z, 1.0);
1226 double prevY = firstY = ProjY(first->trs, midX, midY, z, 1.0);
1227
1228 relevant_frames[0] = first; // Add first frame by default
1229 relevant_frames_size = 1;
1230
1231 for (int i = 0; i < frames_size - 1; i++)
1232 {
1233 mb = frames[i];
1234 double currX, currY;
1235 z = ProjZ(mb->trs, midX, midY, 1.0);
1236 currX = ProjX(mb->trs, midX, midY, z, 1.0);
1237 currY = ProjY(mb->trs, midX, midY, z, 1.0);
1238 double deltaX = currX - prevX;
1239 double deltaY = currY - prevY;
1240 double center2centerDist = sqrt(deltaY * deltaY + deltaX * deltaX);
1241
1242 if (fabs(deltaX) > STRIP_SEPARATION_THRESHOLD_PXLS ||
1243 fabs(deltaY) > STRIP_SEPARATION_THRESHOLD_PXLS)
1244 {
1245 relevant_frames[relevant_frames_size] = mb;
1246 relevant_frames_size++;
1247
1248 prevX = currX;
1249 prevY = currY;
1250 }
1251 }
1252
1253 // Add last frame by default
1254 relevant_frames[relevant_frames_size] = last;
1255 relevant_frames_size++;
1256 }
1257
ComputeBlendParameters(MosaicFrame ** frames,int frames_size,int is360)1258 void Blend::ComputeBlendParameters(MosaicFrame **frames, int frames_size, int is360)
1259 {
1260 // For FULL and PAN modes, we do not unwarp the mosaic into a rectangular coordinate system
1261 // and so we set the theta to 0 and return.
1262 if (m_wb.blendingType != BLEND_TYPE_CYLPAN && m_wb.blendingType != BLEND_TYPE_HORZ)
1263 {
1264 m_wb.theta = 0.0;
1265 return;
1266 }
1267
1268 MosaicFrame *first = frames[0];
1269 MosaicFrame *last = frames[frames_size-1];
1270 MosaicFrame *mb;
1271
1272 double lxpos = last->trs[0][2], lypos = last->trs[1][2];
1273 double fxpos = first->trs[0][2], fypos = first->trs[1][2];
1274
1275 // Calculate warp to produce proper stitching.
1276 // get x, y displacement
1277 double midX = last->width / 2.0;
1278 double midY = last->height / 2.0;
1279 double z = ProjZ(first->trs, midX, midY, 1.0);
1280 double firstX, firstY;
1281 double prevX = firstX = ProjX(first->trs, midX, midY, z, 1.0);
1282 double prevY = firstY = ProjY(first->trs, midX, midY, z, 1.0);
1283
1284 double arcLength, lastTheta;
1285 m_wb.theta = lastTheta = arcLength = 0.0;
1286
1287 // Step through all the frames to compute the total arc-length of the cone
1288 // swept while capturing the mosaic (in the original conical coordinate system).
1289 for (int i = 0; i < frames_size; i++)
1290 {
1291 mb = frames[i];
1292 double currX, currY;
1293 z = ProjZ(mb->trs, midX, midY, 1.0);
1294 currX = ProjX(mb->trs, midX, midY, z, 1.0);
1295 currY = ProjY(mb->trs, midX, midY, z, 1.0);
1296 double deltaX = currX - prevX;
1297 double deltaY = currY - prevY;
1298
1299 // The arcLength is computed by summing the lengths of the chords
1300 // connecting the pairwise projected image centers of the input image frames.
1301 arcLength += sqrt(deltaY * deltaY + deltaX * deltaX);
1302
1303 if (!is360)
1304 {
1305 double thisTheta = asin(mb->trs[1][0]);
1306 m_wb.theta += thisTheta - lastTheta;
1307 lastTheta = thisTheta;
1308 }
1309
1310 prevX = currX;
1311 prevY = currY;
1312 }
1313
1314 // Stretch this to end at the proper alignment i.e. the width of the
1315 // rectangle is determined by the arcLength computed above and the cone
1316 // sector angle is determined using the rotation of the last frame.
1317 m_wb.width = arcLength;
1318 if (is360) m_wb.theta = asin(last->trs[1][0]);
1319
1320 // If there is no rotation, we're done.
1321 if (m_wb.theta != 0.0)
1322 {
1323 double dx = prevX - firstX;
1324 double dy = prevY - firstY;
1325
1326 // If the mosaic was captured by sweeping horizontally
1327 if (abs(lxpos - fxpos) > abs(lypos - fypos))
1328 {
1329 m_wb.horizontal = 1;
1330 // Calculate radius position to make ends exactly the same Y offset
1331 double radiusTheta = dx / cos(3.14159 / 2.0 - m_wb.theta);
1332 m_wb.radius = dy + radiusTheta * cos(m_wb.theta);
1333 if (m_wb.radius < 0.0) m_wb.radius = -m_wb.radius;
1334 }
1335 else
1336 {
1337 m_wb.horizontal = 0;
1338 // Calculate radius position to make ends exactly the same Y offset
1339 double radiusTheta = dy / cos(3.14159 / 2.0 - m_wb.theta);
1340 m_wb.radius = dx + radiusTheta * cos(m_wb.theta);
1341 if (m_wb.radius < 0.0) m_wb.radius = -m_wb.radius;
1342 }
1343
1344 // Determine major direction
1345 if (m_wb.horizontal)
1346 {
1347 // Horizontal strip
1348 // m_wb.x,y record the origin of the rectangle coordinate system.
1349 if (is360) m_wb.x = firstX;
1350 else
1351 {
1352 if (lxpos - fxpos < 0)
1353 {
1354 m_wb.x = firstX + midX;
1355 z = ProjZ(last->trs, 0.0, midY, 1.0);
1356 prevX = ProjX(last->trs, 0.0, midY, z, 1.0);
1357 prevY = ProjY(last->trs, 0.0, midY, z, 1.0);
1358 }
1359 else
1360 {
1361 m_wb.x = firstX - midX;
1362 z = ProjZ(last->trs, last->width - 1.0, midY, 1.0);
1363 prevX = ProjX(last->trs, last->width - 1.0, midY, z, 1.0);
1364 prevY = ProjY(last->trs, last->width - 1.0, midY, z, 1.0);
1365 }
1366 }
1367 dy = prevY - firstY;
1368 if (dy < 0.0) m_wb.direction = 1.0;
1369 else m_wb.direction = -1.0;
1370 m_wb.y = firstY - m_wb.radius * m_wb.direction;
1371 if (dy * m_wb.theta > 0.0) m_wb.width = -m_wb.width;
1372 }
1373 else
1374 {
1375 // Vertical strip
1376 if (is360) m_wb.y = firstY;
1377 else
1378 {
1379 if (lypos - fypos < 0)
1380 {
1381 m_wb.x = firstY + midY;
1382 z = ProjZ(last->trs, midX, 0.0, 1.0);
1383 prevX = ProjX(last->trs, midX, 0.0, z, 1.0);
1384 prevY = ProjY(last->trs, midX, 0.0, z, 1.0);
1385 }
1386 else
1387 {
1388 m_wb.x = firstX - midX;
1389 z = ProjZ(last->trs, midX, last->height - 1.0, 1.0);
1390 prevX = ProjX(last->trs, midX, last->height - 1.0, z, 1.0);
1391 prevY = ProjY(last->trs, midX, last->height - 1.0, z, 1.0);
1392 }
1393 }
1394 dx = prevX - firstX;
1395 if (dx < 0.0) m_wb.direction = 1.0;
1396 else m_wb.direction = -1.0;
1397 m_wb.x = firstX - m_wb.radius * m_wb.direction;
1398 if (dx * m_wb.theta > 0.0) m_wb.width = -m_wb.width;
1399 }
1400
1401 // Calculate the correct correction factor
1402 double deltaX = prevX - m_wb.x;
1403 double deltaY = prevY - m_wb.y;
1404 double length = sqrt(deltaX * deltaX + deltaY * deltaY);
1405 double deltaTheta = (m_wb.horizontal) ? deltaX : deltaY;
1406 deltaTheta = asin(deltaTheta / length);
1407 m_wb.correction = ((m_wb.radius - length) * m_wb.direction) /
1408 (deltaTheta / m_wb.theta);
1409 }
1410 }
1411