Home | History | Annotate | Download | only in interface
      1 /*
      2  * stitcher.cpp - stitcher base
      3  *
      4  *  Copyright (c) 2017 Intel Corporation
      5  *
      6  * Licensed under the Apache License, Version 2.0 (the "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *      http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  *
     18  * Author: Wind Yuan <feng.yuan (at) intel.com>
     19  * Author: Yinhang Liu <yinhangx.liu (at) intel.com>
     20  */
     21 
     22 #include "stitcher.h"
     23 #include "xcam_utils.h"
     24 
     25 // angle to position, output range [-180, 180]
     26 #define OUT_WINDOWS_START 0.0f
     27 
     28 #define constraint_margin (2 * _alignment_x)
     29 
     30 #define XCAM_GL_RESTART_FIXED_INDEX 0xFFFF
     31 
     32 namespace XCam {
     33 
     34 static inline bool
     35 merge_neighbor_area (
     36     const Stitcher::CopyArea &current,
     37     const Stitcher::CopyArea &next,
     38     Stitcher::CopyArea &merged)
     39 {
     40     if (current.in_idx == next.in_idx &&
     41             current.in_area.pos_x + current.in_area.width == next.in_area.pos_x &&
     42             current.out_area.pos_x + current.out_area.width == next.out_area.pos_x)
     43     {
     44         merged = current;
     45         merged.in_area.pos_x = current.in_area.pos_x;
     46         merged.in_area.width = current.in_area.width + next.in_area.width;
     47         merged.out_area.pos_x = current.out_area.pos_x;
     48         merged.out_area.width = current.out_area.width + next.out_area.width;
     49         return true;
     50     }
     51     return false;
     52 }
     53 
     54 static inline bool
     55 split_area_by_out (
     56     const Stitcher::CopyArea &area, const uint32_t round_width,
     57     Stitcher::CopyArea &split_a,  Stitcher::CopyArea &split_b)
     58 {
     59     XCAM_ASSERT (area.out_area.pos_x >= 0 && area.out_area.pos_x < (int32_t)round_width);
     60     XCAM_ASSERT (area.out_area.width > 0 && area.out_area.width < (int32_t)round_width);
     61     if (area.out_area.pos_x + area.out_area.width > (int32_t)round_width) {
     62         split_a = area;
     63         split_a.out_area.width = round_width - area.out_area.pos_x;
     64         split_a.in_area.width = split_a.out_area.width;
     65 
     66         split_b = area;
     67         split_b.in_area.pos_x = area.in_area.pos_x + split_a.in_area.width;
     68         split_b.in_area.width = area.in_area.width - split_a.in_area.width;
     69         split_b.out_area.pos_x = 0;
     70         split_b.out_area.width = split_b.in_area.width;
     71         XCAM_ASSERT (split_b.out_area.width == area.out_area.pos_x + area.out_area.width - (int32_t)round_width);
     72         return true;
     73 
     74     }
     75     XCAM_ASSERT (area.out_area.width == area.in_area.width);
     76     return false;
     77 }
     78 
     79 Stitcher::Stitcher (uint32_t align_x, uint32_t align_y)
     80     : _is_crop_set (false)
     81     , _alignment_x (align_x)
     82     , _alignment_y (align_y)
     83     , _output_width (0)
     84     , _output_height (0)
     85     , _out_start_angle (OUT_WINDOWS_START)
     86     , _camera_num (0)
     87     , _is_round_view_set (false)
     88     , _is_overlap_set (false)
     89     , _is_center_marked (false)
     90 {
     91     XCAM_ASSERT (align_x >= 1);
     92     XCAM_ASSERT (align_y >= 1);
     93 }
     94 
     95 Stitcher::~Stitcher ()
     96 {
     97 }
     98 
     99 bool
    100 Stitcher::set_bowl_config (const BowlDataConfig &config)
    101 {
    102     _bowl_config = config;
    103     return true;
    104 }
    105 
    106 bool
    107 Stitcher::set_camera_num (uint32_t num)
    108 {
    109     XCAM_FAIL_RETURN (
    110         ERROR, num <= XCAM_STITCH_MAX_CAMERAS, false,
    111         "stitcher: set camera count failed, num(%d) is larger than max value(%d)",
    112         num, XCAM_STITCH_MAX_CAMERAS);
    113     _camera_num = num;
    114     return true;
    115 }
    116 
    117 bool
    118 Stitcher::set_camera_info (uint32_t index, const CameraInfo &info)
    119 {
    120     XCAM_FAIL_RETURN (
    121         ERROR, index < _camera_num, false,
    122         "stitcher: set camera info failed, index(%d) exceed max camera num(%d)",
    123         index, _camera_num);
    124     _camera_info[index] = info;
    125     return true;
    126 }
    127 
    128 bool
    129 Stitcher::set_crop_info (uint32_t index, const ImageCropInfo &info)
    130 {
    131     XCAM_FAIL_RETURN (
    132         ERROR, index < _camera_num, false,
    133         "stitcher: set camera info failed, index(%d) exceed max camera num(%d)",
    134         index, _camera_num);
    135     _crop_info[index] = info;
    136     _is_crop_set = true;
    137     return true;
    138 }
    139 
    140 bool
    141 Stitcher::get_crop_info (uint32_t index, ImageCropInfo &info) const
    142 {
    143     XCAM_FAIL_RETURN (
    144         ERROR, index < _camera_num, false,
    145         "stitcher: get crop info failed, index(%d) exceed camera num(%d)",
    146         index, _camera_num);
    147     info = _crop_info[index];
    148     return true;
    149 }
    150 
    151 #if 0
    152 bool
    153 Stitcher::set_overlap_info (uint32_t index, const ImageOverlapInfo &info)
    154 {
    155     XCAM_FAIL_RETURN (
    156         ERROR, index < _camera_num, false,
    157         "stitcher: set overlap info failed, index(%d) exceed max camera num(%d)",
    158         index, _camera_num);
    159     _overlap_info[index] = info;
    160     _is_overlap_set = true;
    161     return true;
    162 }
    163 
    164 bool
    165 Stitcher::get_overlap_info (uint32_t index, ImageOverlapInfo &info) const
    166 {
    167     XCAM_FAIL_RETURN (
    168         ERROR, index < _camera_num, false,
    169         "stitcher: get overlap info failed, index(%d) exceed camera num(%d)",
    170         index, _camera_num);
    171     info = _overlap_info[index];
    172     return true;
    173 }
    174 #endif
    175 
    176 bool
    177 Stitcher::get_camera_info (uint32_t index, CameraInfo &info) const
    178 {
    179     XCAM_FAIL_RETURN (
    180         ERROR, index < XCAM_STITCH_MAX_CAMERAS, false,
    181         "stitcher: get camera info failed, index(%d) exceed max camera value(%d)",
    182         index, XCAM_STITCH_MAX_CAMERAS);
    183     info = _camera_info[index];
    184     return true;
    185 }
    186 
    187 XCamReturn
    188 Stitcher::estimate_round_slices ()
    189 {
    190     if (_is_round_view_set)
    191         return XCAM_RETURN_NO_ERROR;
    192 
    193     XCAM_FAIL_RETURN (
    194         ERROR, _camera_num && _camera_num < XCAM_STITCH_MAX_CAMERAS, XCAM_RETURN_ERROR_PARAM,
    195         "stitcher: camera num was not set, or camera num(%d) exceed max camera value(%d)",
    196         _camera_num, XCAM_STITCH_MAX_CAMERAS);
    197 
    198     for (uint32_t i = 0; i < _camera_num; ++i) {
    199         CameraInfo &cam_info = _camera_info[i];
    200         RoundViewSlice &view_slice = _round_view_slices[i];
    201 
    202         view_slice.width = cam_info.angle_range / 360.0f * (float)_output_width;
    203         view_slice.width = XCAM_ALIGN_UP (view_slice.width, _alignment_x);
    204         view_slice.height = _output_height;
    205         view_slice.hori_angle_range = view_slice.width * 360.0f / (float)_output_width;
    206 
    207         uint32_t aligned_start = format_angle (cam_info.round_angle_start) / 360.0f * (float)_output_width;
    208         aligned_start = XCAM_ALIGN_AROUND (aligned_start, _alignment_x);
    209         if (_output_width <= constraint_margin + aligned_start || aligned_start <= constraint_margin)
    210             aligned_start = 0;
    211         view_slice.hori_angle_start = format_angle((float)aligned_start / (float)_output_width * 360.0f);
    212         if (XCAM_DOUBLE_EQUAL_AROUND (view_slice.hori_angle_start, 0.0001f))
    213             view_slice.hori_angle_start = 0.0f;
    214 
    215         cam_info.round_angle_start = view_slice.hori_angle_start;
    216         cam_info.angle_range = view_slice.hori_angle_range;
    217     }
    218 
    219     _is_round_view_set = true;
    220     return XCAM_RETURN_NO_ERROR;
    221 }
    222 
    223 XCamReturn
    224 Stitcher::estimate_coarse_crops ()
    225 {
    226     if (_is_crop_set)
    227         return XCAM_RETURN_NO_ERROR;
    228 
    229     XCAM_FAIL_RETURN (
    230         ERROR, _camera_num > 0 && _is_round_view_set, XCAM_RETURN_ERROR_ORDER,
    231         "stitcher mark_centers failed, need set camera info and round_slices first");
    232 
    233     for (uint32_t i = 0; i < _camera_num; ++i) {
    234         _crop_info[i].left = 0;
    235         _crop_info[i].right = 0;
    236         _crop_info[i].top = 0;
    237         _crop_info[i].bottom = 0;
    238     }
    239     _is_crop_set = true;
    240     return XCAM_RETURN_NO_ERROR;
    241 }
    242 
    243 // after crop done
    244 XCamReturn
    245 Stitcher::mark_centers ()
    246 {
    247     if (_is_center_marked)
    248         return XCAM_RETURN_NO_ERROR;
    249 
    250     XCAM_FAIL_RETURN (
    251         ERROR, _camera_num > 0 && _is_round_view_set, XCAM_RETURN_ERROR_ORDER,
    252         "stitcher mark_centers failed, need set camera info and round_view slices first");
    253 
    254     for (uint32_t i = 0; i < _camera_num; ++i) {
    255         const RoundViewSlice &slice = _round_view_slices[i];
    256 
    257         //calcuate final output postion
    258         float center_angle = i * 360.0f / _camera_num;
    259         uint32_t out_pos = format_angle (center_angle - _out_start_angle) / 360.0f * _output_width;
    260         XCAM_ASSERT (out_pos < _output_width);
    261         if (_output_width <= constraint_margin + out_pos || out_pos <= constraint_margin)
    262             out_pos = 0;
    263 
    264         // get slice center angle
    265         center_angle = XCAM_ALIGN_AROUND (out_pos, _alignment_x) / (float)_output_width * 360.0f - _out_start_angle;
    266         center_angle = format_angle (center_angle);
    267 
    268         float center_in_slice = center_angle - slice.hori_angle_start;
    269         center_in_slice = format_angle (center_in_slice);
    270         XCAM_FAIL_RETURN (
    271             ERROR, center_in_slice < slice.hori_angle_range,
    272             XCAM_RETURN_ERROR_PARAM,
    273             "stitcher mark center failed, slice:%d  calculated center-angle:%.2f is out of slice angle(start:%.2f, range:%.2f)",
    274             center_angle, slice.hori_angle_start, slice.hori_angle_range);
    275 
    276         uint32_t slice_pos = (uint32_t)(center_in_slice / slice.hori_angle_range * slice.width);
    277         slice_pos = XCAM_ALIGN_AROUND (slice_pos, _alignment_x);
    278         XCAM_ASSERT (slice_pos > _crop_info[i].left && slice_pos < slice.width - _crop_info[i].right);
    279 
    280         _center_marks[i].slice_center_x = slice_pos;
    281         _center_marks[i].out_center_x = out_pos;
    282     }
    283     _is_center_marked = true;
    284 
    285     return XCAM_RETURN_NO_ERROR;
    286 }
    287 
    288 XCamReturn
    289 Stitcher::estimate_overlap ()
    290 {
    291     if (_is_overlap_set)
    292         return XCAM_RETURN_NO_ERROR;
    293 
    294     XCAM_FAIL_RETURN (
    295         ERROR, _is_round_view_set && _is_crop_set && _is_center_marked, XCAM_RETURN_ERROR_ORDER,
    296         "stitcher estimate_coarse_seam failed, need set round_view slices, crop info and mark centers first");
    297 
    298     for (uint32_t idx = 0; idx < _camera_num; ++idx) {
    299         uint32_t next_idx = (idx + 1) % _camera_num;
    300         const RoundViewSlice &left = _round_view_slices[idx];
    301         const RoundViewSlice &right = _round_view_slices[next_idx];
    302         const CenterMark &left_center = _center_marks[idx];
    303         const CenterMark &right_center = _center_marks[next_idx];
    304         const ImageCropInfo &left_img_crop = _crop_info[idx];
    305         const ImageCropInfo &right_img_crop = _crop_info[next_idx];
    306 
    307 #if 0
    308         XCAM_FAIL_RETURN (
    309             ERROR,
    310             (format_angle (right.hori_angle_start - left.hori_angle_start) < left.hori_angle_range)
    311             XCAM_RETURN_ERROR_UNKNOWN,
    312             "stitcher estimate_coarse_seam failed and there is no seam between slice %d and slice %d", idx, next_idx);
    313 
    314         float seam_angle_start = right.hori_angle_start;
    315         float seam_angle_range =
    316             format_angle (left.hori_angle_start + left.hori_angle_range - right.hori_angle_start);
    317 
    318         XCAM_FAIL_RETURN (
    319             ERROR, seam_angle_range < right.hori_angle_range, XCAM_RETURN_ERROR_UNKNOWN,
    320             "stitcher estimate_coarse_seam failed and left slice(%d)over covered right slice(%d)", idx, next_idx);
    321 
    322         XCAM_ASSERT (!XCAM_DOUBLE_EQUAL_AROUND (left.hori_angle_range, 0.0f));
    323         XCAM_ASSERT (!XCAM_DOUBLE_EQUAL_AROUND (right.hori_angle_range, 0.0f));
    324 #endif
    325         uint32_t out_right_center_x = right_center.out_center_x;
    326         if (out_right_center_x == 0)
    327             out_right_center_x = _output_width;
    328 
    329         Rect valid_left_img, valid_right_img;
    330         valid_left_img.pos_x = left_center.slice_center_x;
    331         valid_left_img.width = left.width - left_img_crop.right - valid_left_img.pos_x;
    332         valid_left_img.pos_y = left_img_crop.top;
    333         valid_left_img.height = left.height - left_img_crop.top - left_img_crop.bottom;
    334 
    335         valid_right_img.width = right_center.slice_center_x - right_img_crop.left;
    336         valid_right_img.pos_x = right_center.slice_center_x - valid_right_img.width;
    337         valid_right_img.pos_y = right_img_crop.top;
    338         valid_right_img.height = right.height - right_img_crop.top - right_img_crop.bottom;
    339 
    340         uint32_t merge_width = out_right_center_x - left_center.out_center_x;
    341         XCAM_FAIL_RETURN (
    342             ERROR,
    343             valid_left_img.width + valid_right_img.width > (int32_t)merge_width,
    344             XCAM_RETURN_ERROR_UNKNOWN,
    345             "stitcher estimate_overlap failed and there is no overlap area between slice %d and slice %d", idx, next_idx);
    346 
    347         uint32_t overlap_width = valid_left_img.width + valid_right_img.width - merge_width;
    348 
    349         Rect left_img_overlap, right_img_overlap;
    350         left_img_overlap.pos_x = valid_left_img.pos_x + valid_left_img.width - overlap_width;
    351         left_img_overlap.width = overlap_width;
    352         left_img_overlap.pos_y = valid_left_img.pos_y;
    353         left_img_overlap.height = valid_left_img.height;
    354         XCAM_ASSERT (left_img_overlap.pos_x >= (int32_t)left_center.slice_center_x &&  left_img_overlap.pos_x < (int32_t)left.width);
    355 
    356         right_img_overlap.pos_x = valid_right_img.pos_x;
    357         right_img_overlap.width = overlap_width;
    358         right_img_overlap.pos_y = valid_right_img.pos_y;
    359         right_img_overlap.height = valid_right_img.height;
    360         XCAM_ASSERT (right_img_overlap.pos_x >= (int32_t)right_img_crop.left && right_img_overlap.pos_x < (int32_t)right_center.slice_center_x);
    361 
    362         Rect out_overlap;
    363         out_overlap.pos_x = left_center.out_center_x + valid_left_img.width - overlap_width;
    364         out_overlap.width = overlap_width;
    365         // out_overlap.pos_y/height not useful by now
    366         out_overlap.pos_y = valid_left_img.pos_y;
    367         out_overlap.height = valid_left_img.height;
    368 
    369 #if 0
    370         left_img_seam.pos_x =
    371             left.width * format_angle (seam_angle_start - left.hori_angle_start) / left.hori_angle_range;
    372         left_img_seam.pos_y = _crop_info[idx].top;
    373         left_img_seam.width = left.width * seam_angle_range / left.hori_angle_range;
    374         left_img_seam.height = left.height - _crop_info[idx].top - _crop_info[idx].bottom;
    375 
    376         //consider crop
    377         XCAM_ASSERT (left_img_seam.pos_x <  left.width - _crop_info[idx].right);
    378         if (left_img_seam.pos_x + left_img_seam.width > left.width - _crop_info[idx].right)
    379             left_img_seam.width = left.width - _crop_info[idx].right;
    380 
    381         right_img_seam.pos_x = 0;
    382         right_img_seam.pos_y = _crop_info[next_idx].top;
    383         right_img_seam.width = right.width * (seam_angle_range / right.hori_angle_range);
    384         right_img_seam.height = right.height - _crop_info[next_idx].top - _crop_info[next_idx].bottom;
    385 
    386         //consider crop
    387         XCAM_ASSERT (right_img_seam.pos_x + right_img_seam.width >  _crop_info[next_idx].left);
    388         if (_crop_info[next_idx].left) {
    389             right_img_seam.pos_x = _crop_info[next_idx].left;
    390             right_img_seam.width -= _crop_info[next_idx].left;
    391             left_img_seam.pos_x += _crop_info[next_idx].left;
    392             left_img_seam.width -= _crop_info[next_idx].left;
    393         }
    394 
    395         XCAM_ASSERT (abs (left_img_seam.width - right_img_seam.width) < 16);
    396         left_img_seam.pos_x = XCAM_ALIGN_DOWN (left_img_seam.pos_x, _alignment_x);
    397         right_img_seam.pos_x = XCAM_ALIGN_DOWN (right_img_seam.pos_x, _alignment_x);
    398 
    399         //find max seam width
    400         uint32_t seam_width, seam_height;
    401         seam_width = XCAM_MAX (left_img_seam.width, right_img_seam.width);
    402         if (left_img_seam.pos_x + seam_width > left.width)
    403             seam_width = left.width - left_img_seam.pos_x;
    404         if (right_img_seam.pos_x + seam_width > right.width)
    405             seam_width = right.width - right_img_seam.pos_x;
    406 
    407         XCAM_FAIL_RETURN (
    408             ERROR, seam_width >= XCAM_STITCH_MIN_SEAM_WIDTH, XCAM_RETURN_ERROR_UNKNOWN,
    409             "stitcher estimate_coarse_seam failed, the seam(w:%d) is very narrow between(slice %d and %d)",
    410             seam_width, idx, next_idx);
    411         left_img_seam.width = right_img_seam.width = XCAM_ALIGN_DOWN (seam_width, _alignment_x);
    412 
    413         // min height
    414         uint32_t top = XCAM_MAX (left_img_seam.pos_y, right_img_seam.pos_y);
    415         uint32_t bottom0 = left_img_seam.pos_y + left_img_seam.height;
    416         uint32_t bottom1 = right_img_seam.pos_y + right_img_seam.height;
    417         uint32_t bottom = XCAM_MIN (bottom0, bottom1);
    418         top = XCAM_ALIGN_UP (top, _alignment_y);
    419         left_img_seam.pos_y = right_img_seam.pos_y = top;
    420         left_img_seam.height = right_img_seam.height = XCAM_ALIGN_DOWN (bottom - top, _alignment_y);
    421 #endif
    422         // set overlap info
    423         _overlap_info[idx].left = left_img_overlap;
    424         _overlap_info[idx].right = right_img_overlap;
    425         _overlap_info[idx].out_area = out_overlap;
    426     }
    427 
    428     _is_overlap_set = true;
    429 
    430     return XCAM_RETURN_NO_ERROR;
    431 }
    432 
    433 XCamReturn
    434 Stitcher::update_copy_areas ()
    435 {
    436     XCAM_FAIL_RETURN (
    437         ERROR, _camera_num > 1 && _is_round_view_set && _is_crop_set && _is_overlap_set, XCAM_RETURN_ERROR_ORDER,
    438         "stitcher update_copy_areas failed, check orders, need"
    439         "camera_info, round_view slices, crop_info and overlap_info set first.");
    440 
    441     CopyAreaArray tmp_areas;
    442     uint32_t i = 0;
    443     uint32_t next_i = 0;
    444     for (i = 0; i < _camera_num; ++i) {
    445         next_i = (i + 1 ) % _camera_num;
    446         const CenterMark &mark_left = _center_marks[i];
    447         const CenterMark &mark_right = _center_marks[next_i];
    448         const ImageOverlapInfo  &overlap = _overlap_info[i];
    449 
    450         CopyArea split_a, split_b;
    451 
    452         CopyArea left;
    453         left.in_idx = i;
    454         left.in_area.pos_x = mark_left.slice_center_x;
    455         left.in_area.width = overlap.left.pos_x - left.in_area.pos_x;
    456         XCAM_ASSERT (left.in_area.width > 0);
    457         left.in_area.pos_y = _crop_info[i].top;
    458         left.in_area.height = _round_view_slices[i].height - _crop_info[i].top - _crop_info[i].bottom;
    459         XCAM_ASSERT (left.in_area.height > 0);
    460 
    461         left.out_area.pos_x = mark_left.out_center_x;
    462         left.out_area.width = left.in_area.width;
    463         left.out_area.pos_y = 0;
    464         left.out_area.height = left.in_area.height;
    465 
    466         if (split_area_by_out (left, _output_width, split_a, split_b)) {
    467             tmp_areas.push_back (split_a);
    468             tmp_areas.push_back (split_b);
    469         } else {
    470             tmp_areas.push_back (left);
    471         }
    472 
    473         CopyArea right;
    474         right.in_idx = next_i;
    475         right.in_area.pos_x = _overlap_info[i].right.pos_x + _overlap_info[i].right.width;
    476         right.in_area.width =  (int32_t)mark_right.slice_center_x - right.in_area.pos_x;
    477         XCAM_ASSERT (right.in_area.width > 0);
    478         right.in_area.pos_y = _crop_info[next_i].top;
    479         right.in_area.height = _round_view_slices[next_i].height - _crop_info[next_i].top - _crop_info[next_i].bottom;
    480         XCAM_ASSERT (right.in_area.height > 0);
    481 
    482         uint32_t out_right_center_x = mark_right.out_center_x;
    483         if (out_right_center_x == 0)
    484             out_right_center_x = _output_width;
    485         right.out_area.width = right.in_area.width;
    486         right.out_area.pos_x = out_right_center_x - right.out_area.width;
    487         right.out_area.pos_y = 0;
    488         right.out_area.height = right.in_area.height;
    489 
    490         if (split_area_by_out (right, _output_width, split_a, split_b)) {
    491             tmp_areas.push_back (split_a);
    492             tmp_areas.push_back (split_b);
    493         } else {
    494             tmp_areas.push_back (right);
    495         }
    496     }
    497     XCAM_ASSERT (tmp_areas.size () > _camera_num && _camera_num >= 2);
    498 
    499     CopyArea merged;
    500     int32_t start = 0;
    501     int32_t end = tmp_areas.size () - 1;
    502     if (tmp_areas.size () > 2) {
    503         const CopyArea &first = tmp_areas[0];
    504         const CopyArea &last = tmp_areas[end];
    505         // merge first and last
    506         if (merge_neighbor_area (last, first, merged)) {
    507             _copy_areas.push_back (merged);
    508             ++start;
    509             --end;
    510         }
    511     }
    512 
    513     // merge areas
    514     for (i = (uint32_t)start; (int32_t)i <= end; ) {
    515         const CopyArea &current = tmp_areas[i];
    516         if (i == (uint32_t)end) {
    517             _copy_areas.push_back (current);
    518             break;
    519         }
    520 
    521         const CopyArea &next = tmp_areas[i + 1];
    522         if (merge_neighbor_area (current, next, merged)) {
    523             _copy_areas.push_back (merged);
    524             i += 2;
    525         } else {
    526             _copy_areas.push_back (current);
    527             i += 1;
    528         }
    529     }
    530 
    531     XCAM_ASSERT (_copy_areas.size() >= _camera_num);
    532 
    533     return XCAM_RETURN_NO_ERROR;
    534 }
    535 
    536 BowlModel::BowlModel (const BowlDataConfig &config, const uint32_t image_width, const uint32_t image_height)
    537     : _config (config)
    538     , _bowl_img_width (image_width)
    539     , _bowl_img_height (image_height)
    540 {
    541     //max area => x/a = y/b
    542     XCAM_ASSERT (fabs(_config.center_z) < _config.c);
    543     float mid = sqrt ((1.0f - _config.center_z * _config.center_z / (_config.c * _config.c)) / 2.0f);
    544     _max_topview_length_mm = mid * _config.a * 2.0f;
    545     _max_topview_width_mm = mid * _config.b * 2.0f;
    546 }
    547 
    548 bool
    549 BowlModel::get_max_topview_area_mm (float &length_mm, float &width_mm)
    550 {
    551     if (_max_topview_width_mm <= 0.0f || _max_topview_length_mm <= 0.0f)
    552         return false;
    553     length_mm = _max_topview_length_mm;
    554     width_mm = _max_topview_width_mm;
    555     return true;
    556 }
    557 
    558 bool
    559 BowlModel::get_topview_rect_map (
    560     PointMap &texture_points,
    561     uint32_t res_width, uint32_t res_height,
    562     float length_mm, float width_mm)
    563 {
    564     if (XCAM_DOUBLE_EQUAL_AROUND (length_mm, 0.0f) ||
    565             XCAM_DOUBLE_EQUAL_AROUND (width_mm, 0.0f)) {
    566         get_max_topview_area_mm (length_mm, width_mm);
    567     }
    568 
    569     XCAM_FAIL_RETURN (
    570         ERROR,
    571         length_mm * length_mm / (_config.a * _config.a) / 4.0f + width_mm * width_mm / (_config.b * _config.b) / 4.0f +
    572         _config.center_z * _config.center_z / (_config.c * _config.c) <= 1.0f + 0.001f,
    573         false,
    574         "bowl model topview input area(L:%.2fmm, W:%.2fmm) is larger than max area", length_mm, width_mm);
    575 
    576     float center_pos_x = res_width / 2.0f;
    577     float center_pos_y = res_height / 2.0f;
    578     float mm_per_pixel_x = length_mm / res_width;
    579     float mm_per_pixel_y = width_mm / res_height;
    580 
    581     texture_points.resize (res_width * res_height);
    582 
    583     for(uint32_t row = 0; row < res_height; row++) {
    584         for(uint32_t col = 0; col < res_width; col++) {
    585             PointFloat3 world_pos (
    586                 (col - center_pos_x) * mm_per_pixel_x,
    587                 (center_pos_y - row) * mm_per_pixel_y,
    588                 0.0f);
    589 
    590             PointFloat2 texture_pos = bowl_view_coords_to_image (
    591                                           _config, world_pos, _bowl_img_width, _bowl_img_height);
    592 
    593             texture_points [res_width * row + col] = texture_pos;
    594         }
    595     }
    596     return true;
    597 }
    598 
    599 bool
    600 BowlModel::get_stitch_image_vertex_model (
    601     VertexMap &vertices, PointMap &texture_points, IndexVector &indeices,
    602     uint32_t res_width, uint32_t res_height, float vertex_height)
    603 {
    604     vertices.reserve (2 * (res_width + 1) * (res_height + 1));
    605     texture_points.reserve (2 * (res_width + 1) * (res_height + 1));
    606     indeices.reserve (2 * (res_width + 1) * (res_height + 1) + (res_height + 1));
    607 
    608     float step_x = (float)_bowl_img_width / res_width;
    609     float step_y = vertex_height / res_height;
    610     float offset_y = (float)_bowl_img_height - vertex_height;
    611 
    612     int32_t indicator = 0;
    613 
    614     for (uint32_t row = 0; row < res_height - 1; row++) {
    615         PointFloat2 texture_pos0;
    616         texture_pos0.y = row * step_y + offset_y;
    617 
    618         PointFloat2 texture_pos1;
    619         texture_pos1.y = (row + 1) * step_y + offset_y;
    620 
    621         for (uint32_t col = 0; col <= res_width; col++) {
    622 
    623             texture_pos0.x = col * step_x;
    624             texture_pos1.x = col * step_x;
    625 
    626             PointFloat3 world_pos0 =
    627                 bowl_view_image_to_world (
    628                     _config, _bowl_img_width, _bowl_img_height, texture_pos0);
    629 
    630             vertices.push_back (PointFloat3(world_pos0.x / _config.a, world_pos0.y / _config.b, world_pos0.z / _config.c));
    631             indeices.push_back (indicator++);
    632             texture_points.push_back (PointFloat2(texture_pos0.x / _bowl_img_width, (_bowl_img_height - texture_pos0.y) / _bowl_img_height));
    633 
    634             PointFloat3 world_pos1 =
    635                 bowl_view_image_to_world (
    636                     _config, _bowl_img_width, _bowl_img_height, texture_pos1);
    637 
    638             vertices.push_back (PointFloat3(world_pos1.x / _config.a, world_pos1.y / _config.b, world_pos1.z / _config.c));
    639             indeices.push_back (indicator++);
    640             texture_points.push_back (PointFloat2(texture_pos1.x / _bowl_img_width, (_bowl_img_height - texture_pos1.y) / _bowl_img_height));
    641         }
    642     }
    643     return true;
    644 }
    645 
    646 
    647 bool
    648 BowlModel::get_bowlview_vertex_model (
    649     VertexMap &vertices, PointMap &texture_points, IndexVector &indeices,
    650     uint32_t res_width, uint32_t res_height)
    651 {
    652     return get_stitch_image_vertex_model (vertices, texture_points, indeices, res_width, res_height, (float)_bowl_img_height);
    653 }
    654 
    655 bool
    656 BowlModel::get_topview_vertex_model (
    657     VertexMap &vertices, PointMap &texture_points, IndexVector &indeices,
    658     uint32_t res_width, uint32_t res_height)
    659 {
    660     float wall_image_height = _config.wall_height / (float)(_config.wall_height + _config.ground_length) * (float)_bowl_img_height;
    661     float ground_image_height = (float)_bowl_img_height - wall_image_height;
    662 
    663     return get_stitch_image_vertex_model (vertices, texture_points, indeices, res_width, res_height, ground_image_height);
    664 }
    665 
    666 
    667 }
    668