stitcher.cpp revision 86f627c22fb0293cd3331cf14bc4c2349757bb10
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@intel.com>
19 * Author: Yinhang Liu <yinhangx.liu@intel.com>
20 */
21
22#include "stitcher.h"
23
24// angle to position, output range [-180, 180]
25#define OUT_WINDOWS_START -180.0f
26
27namespace XCam {
28
29static inline bool
30merge_neighbor_area (
31    const Stitcher::CopyArea &current,
32    const Stitcher::CopyArea &next,
33    Stitcher::CopyArea &merged)
34{
35    if (current.in_idx == next.in_idx &&
36            current.in_area.pos_x + current.in_area.width == next.in_area.pos_x &&
37            current.out_area.pos_x + current.out_area.width == next.out_area.pos_x)
38    {
39        merged = current;
40        merged.in_area.pos_x = current.in_area.pos_x;
41        merged.in_area.width = current.in_area.width + next.in_area.width;
42        merged.out_area.pos_x = current.out_area.pos_x;
43        merged.out_area.width = current.out_area.width + next.out_area.width;
44        return true;
45    }
46    return false;
47}
48
49static inline bool
50split_area_by_out (
51    const Stitcher::CopyArea &area, const uint32_t round_width,
52    Stitcher::CopyArea &split_a,  Stitcher::CopyArea &split_b)
53{
54    XCAM_ASSERT (area.out_area.pos_x >= 0 && area.out_area.pos_x < (int32_t)round_width);
55    XCAM_ASSERT (area.out_area.width > 0 && area.out_area.width < (int32_t)round_width);
56    if (area.out_area.pos_x + area.out_area.width > (int32_t)round_width) {
57        split_a = area;
58        split_a.out_area.width = round_width - area.out_area.pos_x;
59        split_a.in_area.width = split_a.out_area.width;
60
61        split_b = area;
62        split_b.in_area.pos_x = area.in_area.pos_x + split_a.in_area.width;
63        split_b.in_area.width = area.in_area.width - split_a.in_area.width;
64        split_b.out_area.pos_x = 0;
65        split_b.out_area.width = split_b.in_area.width;
66        XCAM_ASSERT (split_b.out_area.width == area.out_area.pos_x + area.out_area.width - (int32_t)round_width);
67        return true;
68
69    }
70    XCAM_ASSERT (area.out_area.width == area.in_area.width);
71    return false;
72}
73
74Stitcher::Stitcher (uint32_t align_x, uint32_t align_y)
75    : _is_crop_set (false)
76    , _alignment_x (align_x)
77    , _alignment_y (align_y)
78    , _output_width (0)
79    , _output_height (0)
80    , _out_start_angle (OUT_WINDOWS_START)
81    , _camera_num (0)
82    , _is_overlap_set (false)
83    , _is_center_marked (false)
84{
85    XCAM_ASSERT (align_x >= 1);
86    XCAM_ASSERT (align_y >= 1);
87}
88
89Stitcher::~Stitcher ()
90{
91}
92
93bool
94Stitcher::set_bowl_config (const BowlDataConfig &config)
95{
96    _bowl_config = config;
97    return true;
98}
99
100bool
101Stitcher::set_camera_num (uint32_t num)
102{
103    XCAM_FAIL_RETURN (
104        ERROR, num <= XCAM_STITCH_MAX_CAMERAS, false,
105        "stitcher: set camera count failed, num(%d) is larger than max value(%d)",
106        num, XCAM_STITCH_MAX_CAMERAS);
107    _camera_num = num;
108    return true;
109}
110
111bool
112Stitcher::set_camera_info (uint32_t index, const CameraInfo &info)
113{
114    XCAM_FAIL_RETURN (
115        ERROR, index < _camera_num, false,
116        "stitcher: set camera info failed, index(%d) exceed max camera num(%d)",
117        index, _camera_num);
118    _camera_info[index] = info;
119    return true;
120}
121
122bool
123Stitcher::set_crop_info (uint32_t index, const ImageCropInfo &info)
124{
125    XCAM_FAIL_RETURN (
126        ERROR, index < _camera_num, false,
127        "stitcher: set camera info failed, index(%d) exceed max camera num(%d)",
128        index, _camera_num);
129    _crop_info[index] = info;
130    _is_crop_set = true;
131    return true;
132}
133
134bool
135Stitcher::get_crop_info (uint32_t index, ImageCropInfo &info) const
136{
137    XCAM_FAIL_RETURN (
138        ERROR, index < _camera_num, false,
139        "stitcher: get crop info failed, index(%d) exceed camera num(%d)",
140        index, _camera_num);
141    info = _crop_info[index];
142    return true;
143}
144
145#if 0
146bool
147Stitcher::set_overlap_info (uint32_t index, const ImageOverlapInfo &info)
148{
149    XCAM_FAIL_RETURN (
150        ERROR, index < _camera_num, false,
151        "stitcher: set overlap info failed, index(%d) exceed max camera num(%d)",
152        index, _camera_num);
153    _overlap_info[index] = info;
154    _is_overlap_set = true;
155    return true;
156}
157
158bool
159Stitcher::get_overlap_info (uint32_t index, ImageOverlapInfo &info) const
160{
161    XCAM_FAIL_RETURN (
162        ERROR, index < _camera_num, false,
163        "stitcher: get overlap info failed, index(%d) exceed camera num(%d)",
164        index, _camera_num);
165    info = _overlap_info[index];
166    return true;
167}
168#endif
169
170bool
171Stitcher::get_camera_info (uint32_t index, CameraInfo &info) const
172{
173    XCAM_FAIL_RETURN (
174        ERROR, index < XCAM_STITCH_MAX_CAMERAS, false,
175        "stitcher: get camera info failed, index(%d) exceed max camera value(%d)",
176        index, XCAM_STITCH_MAX_CAMERAS);
177    info = _camera_info[index];
178    return true;
179}
180
181XCamReturn
182Stitcher::estimate_coarse_crops ()
183{
184    if (_is_crop_set)
185        return XCAM_RETURN_NO_ERROR;
186
187    for (uint32_t i = 0; i < _camera_num; ++i) {
188        _crop_info[i].left = 0;
189        _crop_info[i].right = 0;
190        _crop_info[i].top = 0;
191        _crop_info[i].bottom = 0;
192    }
193    _is_crop_set = true;
194    return XCAM_RETURN_NO_ERROR;
195}
196
197// after crop done
198XCamReturn
199Stitcher::mark_centers ()
200{
201    const uint32_t constraint_margin = 2 * _alignment_x;
202
203    if (_is_center_marked)
204        return XCAM_RETURN_NO_ERROR;
205
206    XCAM_FAIL_RETURN (
207        ERROR, _camera_num > 0, XCAM_RETURN_ERROR_ORDER,
208        "stitcher mark_centers failed, need set camera info first");
209
210    for (uint32_t i = 0; i < _camera_num; ++i) {
211        const RoundViewSlice &slice = _camera_info[i].slice_view;
212
213        //calcuate final output postion
214        float center_angle = i * 360.0f / _camera_num;
215        uint32_t out_pos = format_angle (center_angle - _out_start_angle) / 360.0f * _output_width;
216        XCAM_ASSERT (out_pos < _output_width);
217        if (_output_width - out_pos < constraint_margin || out_pos < constraint_margin)
218            out_pos = 0;
219
220        // get slice center angle
221        center_angle = XCAM_ALIGN_AROUND (out_pos, _alignment_x) / (float)_output_width * 360.0f - _out_start_angle;
222        center_angle = format_angle (center_angle);
223
224        float center_in_slice = center_angle - slice.hori_angle_start;
225        center_in_slice = format_angle (center_in_slice);
226        XCAM_FAIL_RETURN (
227            ERROR, center_in_slice < slice.hori_angle_range,
228            XCAM_RETURN_ERROR_PARAM,
229            "stitcher mark center failed, slice:%d  calculated center-angle:%.2f is out of slice angle(start:%.2f, range:%.2f)",
230            center_angle, slice.hori_angle_start, slice.hori_angle_range);
231
232        uint32_t slice_pos = (uint32_t)(center_in_slice / slice.hori_angle_range * slice.width);
233        slice_pos = XCAM_ALIGN_AROUND (slice_pos, _alignment_x);
234        XCAM_ASSERT (slice_pos > _crop_info[i].left && slice_pos < slice.width - _crop_info[i].right);
235
236        _center_marks[i].slice_center_x = slice_pos;
237        _center_marks[i].out_center_x = out_pos;
238    }
239    _is_center_marked = true;
240
241    return XCAM_RETURN_NO_ERROR;
242}
243
244XCamReturn
245Stitcher::estimate_overlap ()
246{
247    if (_is_overlap_set)
248        return XCAM_RETURN_NO_ERROR;
249
250    XCAM_FAIL_RETURN (
251        ERROR, _is_crop_set && _is_center_marked, XCAM_RETURN_ERROR_ORDER,
252        "stitcher estimate_coarse_seam failed, need set crop info first");
253
254    for (uint32_t idx = 0; idx < _camera_num; ++idx) {
255        uint32_t next_idx = (idx + 1) % _camera_num;
256        const RoundViewSlice &left = _camera_info[idx].slice_view;
257        const RoundViewSlice &right = _camera_info[next_idx].slice_view;
258        const CenterMark &left_center = _center_marks[idx];
259        const CenterMark &right_center = _center_marks[next_idx];
260        const ImageCropInfo &left_img_crop = _crop_info[idx];
261        const ImageCropInfo &right_img_crop = _crop_info[next_idx];
262
263#if 0
264        XCAM_FAIL_RETURN (
265            ERROR,
266            (format_angle (right.hori_angle_start - left.hori_angle_start) < left.hori_angle_range)
267            XCAM_RETURN_ERROR_UNKNOWN,
268            "stitcher estimate_coarse_seam failed and there is no seam between slice %d and slice %d", idx, next_idx);
269
270        float seam_angle_start = right.hori_angle_start;
271        float seam_angle_range =
272            format_angle (left.hori_angle_start + left.hori_angle_range - right.hori_angle_start);
273
274        XCAM_FAIL_RETURN (
275            ERROR, seam_angle_range < right.hori_angle_range, XCAM_RETURN_ERROR_UNKNOWN,
276            "stitcher estimate_coarse_seam failed and left slice(%d)over covered right slice(%d)", idx, next_idx);
277
278        XCAM_ASSERT (!XCAM_DOUBLE_EQUAL_AROUND (left.hori_angle_range, 0.0f));
279        XCAM_ASSERT (!XCAM_DOUBLE_EQUAL_AROUND (right.hori_angle_range, 0.0f));
280#endif
281        uint32_t out_right_center_x = right_center.out_center_x;
282        if (out_right_center_x == 0)
283            out_right_center_x = _output_width;
284
285        Rect valid_left_img, valid_right_img;
286        valid_left_img.pos_x = left_center.slice_center_x;
287        valid_left_img.width = left.width - left_img_crop.right - valid_left_img.pos_x;
288        valid_left_img.pos_y = left_img_crop.top;
289        valid_left_img.height = left.height - left_img_crop.top - left_img_crop.bottom;
290
291        valid_right_img.width = right_center.slice_center_x - right_img_crop.left;
292        valid_right_img.pos_x = right_center.slice_center_x - valid_right_img.width;
293        valid_right_img.pos_y = right_img_crop.top;
294        valid_right_img.height = right.height - right_img_crop.top - right_img_crop.bottom;
295
296        uint32_t merge_width = out_right_center_x - left_center.out_center_x;
297        XCAM_FAIL_RETURN (
298            ERROR,
299            valid_left_img.width + valid_right_img.width > (int32_t)merge_width,
300            XCAM_RETURN_ERROR_UNKNOWN,
301            "stitcher estimate_overlap failed and there is no overlap area between slice %d and slice %d", idx, next_idx);
302
303        uint32_t overlap_width = valid_left_img.width + valid_right_img.width - merge_width;
304
305        Rect left_img_overlap, right_img_overlap;
306        left_img_overlap.pos_x = valid_left_img.pos_x + valid_left_img.width - overlap_width;
307        left_img_overlap.width = overlap_width;
308        left_img_overlap.pos_y = valid_left_img.pos_y;
309        left_img_overlap.height = valid_left_img.height;
310        XCAM_ASSERT (left_img_overlap.pos_x >= (int32_t)left_center.slice_center_x &&  left_img_overlap.pos_x < (int32_t)left.width);
311
312        right_img_overlap.pos_x = valid_right_img.pos_x;
313        right_img_overlap.width = overlap_width;
314        right_img_overlap.pos_y = valid_right_img.pos_y;
315        right_img_overlap.height = valid_right_img.height;
316        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);
317
318        Rect out_overlap;
319        out_overlap.pos_x = left_center.out_center_x + valid_left_img.width - overlap_width;
320        out_overlap.width = overlap_width;
321        // out_overlap.pos_y/height not useful by now
322        out_overlap.pos_y = valid_left_img.pos_y;
323        out_overlap.height = valid_left_img.height;
324
325#if 0
326        left_img_seam.pos_x =
327            left.width * format_angle (seam_angle_start - left.hori_angle_start) / left.hori_angle_range;
328        left_img_seam.pos_y = _crop_info[idx].top;
329        left_img_seam.width = left.width * seam_angle_range / left.hori_angle_range;
330        left_img_seam.height = left.height - _crop_info[idx].top - _crop_info[idx].bottom;
331
332        //consider crop
333        XCAM_ASSERT (left_img_seam.pos_x <  left.width - _crop_info[idx].right);
334        if (left_img_seam.pos_x + left_img_seam.width > left.width - _crop_info[idx].right)
335            left_img_seam.width = left.width - _crop_info[idx].right;
336
337        right_img_seam.pos_x = 0;
338        right_img_seam.pos_y = _crop_info[next_idx].top;
339        right_img_seam.width = right.width * (seam_angle_range / right.hori_angle_range);
340        right_img_seam.height = right.height - _crop_info[next_idx].top - _crop_info[next_idx].bottom;
341
342        //consider crop
343        XCAM_ASSERT (right_img_seam.pos_x + right_img_seam.width >  _crop_info[next_idx].left);
344        if (_crop_info[next_idx].left) {
345            right_img_seam.pos_x = _crop_info[next_idx].left;
346            right_img_seam.width -= _crop_info[next_idx].left;
347            left_img_seam.pos_x += _crop_info[next_idx].left;
348            left_img_seam.width -= _crop_info[next_idx].left;
349        }
350
351        XCAM_ASSERT (abs (left_img_seam.width - right_img_seam.width) < 16);
352        left_img_seam.pos_x = XCAM_ALIGN_DOWN (left_img_seam.pos_x, _alignment_x);
353        right_img_seam.pos_x = XCAM_ALIGN_DOWN (right_img_seam.pos_x, _alignment_x);
354
355        //find max seam width
356        uint32_t seam_width, seam_height;
357        seam_width = XCAM_MAX (left_img_seam.width, right_img_seam.width);
358        if (left_img_seam.pos_x + seam_width > left.width)
359            seam_width = left.width - left_img_seam.pos_x;
360        if (right_img_seam.pos_x + seam_width > right.width)
361            seam_width = right.width - right_img_seam.pos_x;
362
363        XCAM_FAIL_RETURN (
364            ERROR, seam_width >= XCAM_STITCH_MIN_SEAM_WIDTH, XCAM_RETURN_ERROR_UNKNOWN,
365            "stitcher estimate_coarse_seam failed, the seam(w:%d) is very narrow between(slice %d and %d)",
366            seam_width, idx, next_idx);
367        left_img_seam.width = right_img_seam.width = XCAM_ALIGN_DOWN (seam_width, _alignment_x);
368
369        // min height
370        uint32_t top = XCAM_MAX (left_img_seam.pos_y, right_img_seam.pos_y);
371        uint32_t bottom0 = left_img_seam.pos_y + left_img_seam.height;
372        uint32_t bottom1 = right_img_seam.pos_y + right_img_seam.height;
373        uint32_t bottom = XCAM_MIN (bottom0, bottom1);
374        top = XCAM_ALIGN_UP (top, _alignment_y);
375        left_img_seam.pos_y = right_img_seam.pos_y = top;
376        left_img_seam.height = right_img_seam.height = XCAM_ALIGN_DOWN (bottom - top, _alignment_y);
377#endif
378        // set overlap info
379        _overlap_info[idx].left = left_img_overlap;
380        _overlap_info[idx].right = right_img_overlap;
381        _overlap_info[idx].out_area = out_overlap;
382    }
383
384    _is_overlap_set = true;
385
386    return XCAM_RETURN_NO_ERROR;
387}
388
389XCamReturn
390Stitcher::update_copy_areas ()
391{
392    XCAM_FAIL_RETURN (
393        ERROR, _camera_num > 1 && _is_crop_set && _is_overlap_set, XCAM_RETURN_ERROR_ORDER,
394        "stitcher update_copy_areas failed, check orders, need camera_info, crop_info and overlap_info set first.");
395
396    CopyAreaArray tmp_areas;
397    uint32_t i = 0;
398    uint32_t next_i = 0;
399    for (i = 0; i < _camera_num; ++i) {
400        next_i = (i + 1 ) % _camera_num;
401        const CenterMark &mark_left = _center_marks[i];
402        const CenterMark &mark_right = _center_marks[next_i];
403        const ImageOverlapInfo  &overlap = _overlap_info[i];
404
405        CopyArea split_a, split_b;
406
407        CopyArea left;
408        left.in_idx = i;
409        left.in_area.pos_x = mark_left.slice_center_x;
410        left.in_area.width = overlap.left.pos_x - left.in_area.pos_x;
411        XCAM_ASSERT (left.in_area.width > 0);
412        left.in_area.pos_y = _crop_info[i].top;
413        left.in_area.height = _camera_info[i].slice_view.height - _crop_info[i].top - _crop_info[i].bottom;
414        XCAM_ASSERT (left.in_area.height > 0);
415
416        left.out_area.pos_x = mark_left.out_center_x;
417        left.out_area.width = left.in_area.width;
418        left.out_area.pos_y = 0;
419        left.out_area.height = left.in_area.height;
420
421        if (split_area_by_out (left, _output_width, split_a, split_b)) {
422            tmp_areas.push_back (split_a);
423            tmp_areas.push_back (split_b);
424        } else {
425            tmp_areas.push_back (left);
426        }
427
428        CopyArea right;
429        right.in_idx = next_i;
430        right.in_area.pos_x = _overlap_info[i].right.pos_x + _overlap_info[i].right.width;
431        right.in_area.width =  (int32_t)mark_right.slice_center_x - right.in_area.pos_x;
432        XCAM_ASSERT (right.in_area.width > 0);
433        right.in_area.pos_y = _crop_info[next_i].top;
434        right.in_area.height = _camera_info[next_i].slice_view.height - _crop_info[next_i].top - _crop_info[next_i].bottom;
435        XCAM_ASSERT (right.in_area.height > 0);
436
437        uint32_t out_right_center_x = mark_right.out_center_x;
438        if (out_right_center_x == 0)
439            out_right_center_x = _output_width;
440        right.out_area.width = right.in_area.width;
441        right.out_area.pos_x = out_right_center_x - right.out_area.width;
442        right.out_area.pos_y = 0;
443        right.out_area.height = right.in_area.height;
444
445        if (split_area_by_out (right, _output_width, split_a, split_b)) {
446            tmp_areas.push_back (split_a);
447            tmp_areas.push_back (split_b);
448        } else {
449            tmp_areas.push_back (right);
450        }
451    }
452    XCAM_ASSERT (tmp_areas.size () > _camera_num && _camera_num >= 2);
453
454    CopyArea merged;
455    int32_t start = 0;
456    int32_t end = tmp_areas.size () - 1;
457    if (tmp_areas.size () > 2) {
458        const CopyArea &first = tmp_areas[0];
459        const CopyArea &last = tmp_areas[end];
460        // merge first and last
461        if (merge_neighbor_area (last, first, merged)) {
462            _copy_areas.push_back (merged);
463            ++start;
464            --end;
465        }
466    }
467
468    // merge areas
469    for (i = (uint32_t)start; (int32_t)i <= end; ) {
470        const CopyArea &current = tmp_areas[i];
471        if (i == (uint32_t)end) {
472            _copy_areas.push_back (current);
473            break;
474        }
475
476        const CopyArea &next = tmp_areas[i + 1];
477        if (merge_neighbor_area (current, next, merged)) {
478            _copy_areas.push_back (merged);
479            i += 2;
480        } else {
481            _copy_areas.push_back (current);
482            i += 1;
483        }
484    }
485
486    XCAM_ASSERT (_copy_areas.size() >= _camera_num);
487
488    return XCAM_RETURN_NO_ERROR;
489}
490
491}
492