• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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