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