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