1/*
2 * cl_image_360_stitch.cpp - CL Image 360 stitch
3 *
4 *  Copyright (c) 2016 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 */
20
21#include "cl_utils.h"
22#include "cl_image_360_stitch.h"
23#if HAVE_OPENCV
24#include "cv_feature_match.h"
25#endif
26
27#define XCAM_BLENDER_GLOBAL_SCALE_EXT_WIDTH 64
28
29#define STITCH_CHECK(ret, msg, ...) \
30    if ((ret) != XCAM_RETURN_NO_ERROR) {        \
31        XCAM_LOG_WARNING (msg, ## __VA_ARGS__); \
32        return ret;                             \
33    }
34
35namespace XCam {
36
37CLBlenderGlobalScaleKernel::CLBlenderGlobalScaleKernel (
38    const SmartPtr<CLContext> &context, SmartPtr<CLImage360Stitch> &stitch, bool is_uv)
39    : CLBlenderScaleKernel (context, is_uv)
40    , _stitch (stitch)
41{
42}
43
44SmartPtr<CLImage>
45CLBlenderGlobalScaleKernel::get_input_image () {
46    SmartPtr<CLContext> context = get_context ();
47    SmartPtr<VideoBuffer> input = _stitch->get_global_scale_input ();
48
49    CLImageDesc cl_desc;
50    SmartPtr<CLImage> cl_image;
51    const VideoBufferInfo &buf_info = input->get_video_info ();
52
53    cl_desc.format.image_channel_data_type = CL_UNORM_INT8;
54    if (_is_uv) {
55        cl_desc.format.image_channel_order = CL_RG;
56        cl_desc.width = buf_info.width / 2;
57        cl_desc.height = buf_info.height / 2;
58        cl_desc.row_pitch = buf_info.strides[1];
59        cl_image = convert_to_climage (context, input, cl_desc, buf_info.offsets[1]);
60    } else {
61        cl_desc.format.image_channel_order = CL_R;
62        cl_desc.width = buf_info.width;
63        cl_desc.height = buf_info.height;
64        cl_desc.row_pitch = buf_info.strides[0];
65        cl_image = convert_to_climage (context, input, cl_desc, buf_info.offsets[0]);
66    }
67
68    return cl_image;
69}
70
71SmartPtr<CLImage>
72CLBlenderGlobalScaleKernel::get_output_image () {
73    SmartPtr<CLContext> context = get_context ();
74    SmartPtr<VideoBuffer> output = _stitch->get_global_scale_output ();
75
76    CLImageDesc cl_desc;
77    SmartPtr<CLImage> cl_image;
78    const VideoBufferInfo &buf_info = output->get_video_info ();
79
80    cl_desc.format.image_channel_data_type = CL_UNSIGNED_INT16;
81    cl_desc.format.image_channel_order = CL_RGBA;
82    if (_is_uv) {
83        cl_desc.width = buf_info.width / 8;
84        cl_desc.height = buf_info.height / 2;
85        cl_desc.row_pitch = buf_info.strides[1];
86        cl_image = convert_to_climage (context, output, cl_desc, buf_info.offsets[1]);
87    } else {
88        cl_desc.width = buf_info.width / 8;
89        cl_desc.height = buf_info.height;
90        cl_desc.row_pitch = buf_info.strides[0];
91        cl_image = convert_to_climage (context, output, cl_desc, buf_info.offsets[0]);
92    }
93
94    return cl_image;
95}
96
97bool
98CLBlenderGlobalScaleKernel::get_output_info (
99    uint32_t &out_width, uint32_t &out_height, int &out_offset_x)
100{
101    SmartPtr<VideoBuffer> output = _stitch->get_global_scale_output ();
102    const VideoBufferInfo &output_info = output->get_video_info ();
103
104    out_width = output_info.width / 8;
105    out_height = _is_uv ? output_info.height / 2 : output_info.height;
106    out_offset_x = 0;
107
108    return true;
109}
110
111#if HAVE_OPENCV
112static CVFMConfig
113get_fm_default_config (StitchResMode res_mode)
114{
115    CVFMConfig config;
116
117    switch (res_mode) {
118    case StitchRes1080P: {
119        config.sitch_min_width = 56;
120        config.min_corners = 8;
121        config.offset_factor = 0.8f;
122        config.delta_mean_offset = 5.0f;
123        config.recur_offset_error = 8.0f;
124        config.max_adjusted_offset = 12.0f;
125        config.max_valid_offset_y = 8.0f;
126        config.max_track_error = 24.0f;
127
128        break;
129    }
130    case StitchRes1080P4: {
131        config.sitch_min_width = 128;
132        config.min_corners = 4;
133        config.offset_factor = 0.8f;
134        config.delta_mean_offset = 24.0f;
135        config.recur_offset_error = 12.0f;
136        config.max_adjusted_offset = 24.0f;
137        config.max_valid_offset_y = 64.0f;
138        config.max_track_error = 32.0f;
139
140        break;
141    }
142    case StitchRes4K: {
143        config.sitch_min_width = 160;
144        config.min_corners = 8;
145        config.offset_factor = 0.8f;
146        config.delta_mean_offset = 5.0f;
147        config.recur_offset_error = 8.0f;
148        config.max_adjusted_offset = 12.0f;
149        config.max_valid_offset_y = 8.0f;
150        config.max_track_error = 24.0f;
151
152        break;
153    }
154    default:
155        XCAM_LOG_DEBUG ("unknown reslution mode (%d)", res_mode);
156        break;
157    }
158
159    return config;
160}
161#endif
162
163static StitchInfo
164get_default_stitch_info (StitchResMode res_mode)
165{
166    StitchInfo stitch_info;
167
168    switch (res_mode) {
169    case StitchRes1080P: {
170        stitch_info.merge_width[0] = 56;
171        stitch_info.merge_width[1] = 56;
172
173        stitch_info.crop[0].left = 96;
174        stitch_info.crop[0].right = 96;
175        stitch_info.crop[0].top = 0;
176        stitch_info.crop[0].bottom = 0;
177        stitch_info.crop[1].left = 96;
178        stitch_info.crop[1].right = 96;
179        stitch_info.crop[1].top = 0;
180        stitch_info.crop[1].bottom = 0;
181
182        stitch_info.fisheye_info[0].center_x = 480.0f;
183        stitch_info.fisheye_info[0].center_y = 480.0f;
184        stitch_info.fisheye_info[0].wide_angle = 202.8f;
185        stitch_info.fisheye_info[0].radius = 480.0f;
186        stitch_info.fisheye_info[0].rotate_angle = -90.0f;
187        stitch_info.fisheye_info[1].center_x = 1440.0f;
188        stitch_info.fisheye_info[1].center_y = 480.0f;
189        stitch_info.fisheye_info[1].wide_angle = 202.8f;
190        stitch_info.fisheye_info[1].radius = 480.0f;
191        stitch_info.fisheye_info[1].rotate_angle = 89.4f;
192        break;
193    }
194    case StitchRes1080P4: {
195        stitch_info.merge_width[0] = 288;
196        stitch_info.merge_width[1] = 288;
197        stitch_info.merge_width[2] = 288;
198        stitch_info.merge_width[3] = 288;
199
200        stitch_info.crop[0].left = 0;
201        stitch_info.crop[0].right = 0;
202        stitch_info.crop[0].top = 0;
203        stitch_info.crop[0].bottom = 0;
204        stitch_info.crop[1].left = 0;
205        stitch_info.crop[1].right = 0;
206        stitch_info.crop[1].top = 0;
207        stitch_info.crop[1].bottom = 0;
208        stitch_info.crop[2].left = 0;
209        stitch_info.crop[2].right = 0;
210        stitch_info.crop[2].top = 0;
211        stitch_info.crop[2].bottom = 0;
212        stitch_info.crop[3].left = 0;
213        stitch_info.crop[3].right = 0;
214        stitch_info.crop[3].top = 0;
215        stitch_info.crop[3].bottom = 0;
216
217        stitch_info.fisheye_info[0].center_x = 640.0f;
218        stitch_info.fisheye_info[0].center_y = 400.0f;
219        stitch_info.fisheye_info[0].wide_angle = 120.0f;
220        stitch_info.fisheye_info[0].radius = 640.0f;
221        stitch_info.fisheye_info[0].rotate_angle = 0.0f;
222        stitch_info.fisheye_info[1].center_x = 640.0f;
223        stitch_info.fisheye_info[1].center_y = 400.0f;
224        stitch_info.fisheye_info[1].wide_angle = 120.0f;
225        stitch_info.fisheye_info[1].radius = 640.0f;
226        stitch_info.fisheye_info[1].rotate_angle = 0.0f;
227        stitch_info.fisheye_info[2].center_x = 640.0f;
228        stitch_info.fisheye_info[2].center_y = 400.0f;
229        stitch_info.fisheye_info[2].wide_angle = 120.0f;
230        stitch_info.fisheye_info[2].radius = 640.0f;
231        stitch_info.fisheye_info[2].rotate_angle = 0.0f;
232        stitch_info.fisheye_info[3].center_x = 640.0f;
233        stitch_info.fisheye_info[3].center_y = 400.0f;
234        stitch_info.fisheye_info[3].wide_angle = 120.0f;
235        stitch_info.fisheye_info[3].radius = 640.0f;
236        stitch_info.fisheye_info[3].rotate_angle = 0.0f;
237        break;
238    }
239    case StitchRes4K: {
240        stitch_info.merge_width[0] = 160;
241        stitch_info.merge_width[1] = 160;
242
243        stitch_info.crop[0].left = 64;
244        stitch_info.crop[0].right = 64;
245        stitch_info.crop[0].top = 0;
246        stitch_info.crop[0].bottom = 0;
247        stitch_info.crop[1].left = 64;
248        stitch_info.crop[1].right = 64;
249        stitch_info.crop[1].top = 0;
250        stitch_info.crop[1].bottom = 0;
251
252        stitch_info.fisheye_info[0].center_x = 1024.0f;
253        stitch_info.fisheye_info[0].center_y = 1024.0f;
254        stitch_info.fisheye_info[0].wide_angle = 195.0f;
255        stitch_info.fisheye_info[0].radius = 1040.0f;
256        stitch_info.fisheye_info[0].rotate_angle = 0.0f;
257
258        stitch_info.fisheye_info[1].center_x = 3072.0f;
259        stitch_info.fisheye_info[1].center_y = 1016.0f;
260        stitch_info.fisheye_info[1].wide_angle = 192.0f;
261        stitch_info.fisheye_info[1].radius = 1040.0f;
262        stitch_info.fisheye_info[1].rotate_angle = 0.4f;
263        break;
264    }
265    default:
266        XCAM_LOG_DEBUG ("unknown reslution mode (%d)", res_mode);
267        break;
268    }
269
270    return stitch_info;
271}
272
273CLImage360Stitch::CLImage360Stitch (
274    const SmartPtr<CLContext> &context, CLBlenderScaleMode scale_mode, SurroundMode surround_mode,
275    StitchResMode res_mode, int fisheye_num, bool all_in_one_img)
276    : CLMultiImageHandler (context, "CLImage360Stitch")
277    , _context (context)
278    , _output_width (0)
279    , _output_height (0)
280    , _scale_mode (scale_mode)
281    , _surround_mode (surround_mode)
282    , _res_mode (res_mode)
283    , _is_stitch_inited (false)
284    , _fisheye_num (fisheye_num)
285    , _all_in_one_img (all_in_one_img)
286{
287#if HAVE_OPENCV
288    for (int i = 0; i < fisheye_num; i++) {
289        _feature_match[i] = new CVFeatureMatch ();
290        XCAM_ASSERT (_feature_match[i].ptr ());
291        _feature_match[i]->set_config (get_fm_default_config (res_mode));
292        _feature_match[i]->set_fm_index (i);
293    }
294#endif
295}
296
297bool
298CLImage360Stitch::set_stitch_info (StitchInfo stitch_info)
299{
300    if (_is_stitch_inited) {
301        XCAM_LOG_WARNING ("stitching info was initialized and can't be set twice");
302        return false;
303    }
304
305    for (int index = 0; index < _fisheye_num; ++index) {
306        _fisheye[index].handler->set_fisheye_info (stitch_info.fisheye_info[index]);
307    }
308
309    _stitch_info = stitch_info;
310    _is_stitch_inited = true;
311
312    return true;
313}
314
315StitchInfo
316CLImage360Stitch::get_stitch_info ()
317{
318    if (!_is_stitch_inited) {
319        XCAM_LOG_WARNING ("stitch-info was not initialized, return default parameters");
320        return get_default_stitch_info (_res_mode);
321    }
322
323    return _stitch_info;
324}
325
326bool
327CLImage360Stitch::set_fisheye_handler (SmartPtr<CLFisheyeHandler> fisheye, int index)
328{
329    XCAM_ASSERT (index < _fisheye_num);
330
331    _fisheye[index].handler = fisheye;
332    SmartPtr<CLImageHandler> handler = fisheye;
333    return add_image_handler (handler);
334}
335
336bool
337CLImage360Stitch::set_blender (SmartPtr<CLBlender> blender, int idx)
338{
339    _blender[idx] = blender;
340
341    SmartPtr<CLImageHandler> handler = blender;
342    return add_image_handler (handler);
343}
344
345void
346CLImage360Stitch::set_fisheye_intrinsic (IntrinsicParameter intrinsic_param, int index)
347{
348    _fisheye[index].handler->set_intrinsic_param(intrinsic_param);
349}
350
351void
352CLImage360Stitch::set_fisheye_extrinsic (ExtrinsicParameter extrinsic_param, int index)
353{
354    _fisheye[index].handler->set_extrinsic_param(extrinsic_param);
355}
356
357const BowlDataConfig &
358CLImage360Stitch::get_fisheye_bowl_config (int index)
359{
360    XCAM_ASSERT (index < _fisheye_num);
361    return _fisheye[index].handler->get_bowl_config ();
362}
363
364bool
365CLImage360Stitch::set_image_overlap (const int idx, const Rect &overlap0, const Rect &overlap1)
366{
367    XCAM_ASSERT (idx < _fisheye_num);
368    _overlaps[idx][0] = overlap0;
369    _overlaps[idx][1] = overlap1;
370    return true;
371}
372
373void
374CLImage360Stitch::set_feature_match_ocl (bool fm_ocl)
375{
376#if HAVE_OPENCV
377    for (int i = 0; i < _fisheye_num; i++) {
378        _feature_match[i]->set_ocl (fm_ocl);
379    }
380#else
381    XCAM_UNUSED (fm_ocl);
382    XCAM_LOG_WARNING ("non-OpenCV mode, failed to set ocl for feature match");
383#endif
384}
385
386#if HAVE_OPENCV
387void
388CLImage360Stitch::set_feature_match_config (const int idx, CVFMConfig config)
389{
390    _feature_match[idx]->set_config (config);
391}
392
393CVFMConfig
394CLImage360Stitch::get_feature_match_config (const int idx)
395{
396    return _feature_match[idx]->get_config ();
397}
398#endif
399
400void
401CLImage360Stitch::calc_fisheye_initial_info (SmartPtr<VideoBuffer> &output)
402{
403    const VideoBufferInfo &out_info = output->get_video_info ();
404
405    if(_surround_mode == SphereView) {
406        uint32_t fisheye_width_sum = out_info.width;
407        for (int i = 0; i < _fisheye_num; i++) {
408            fisheye_width_sum += _stitch_info.merge_width[i] + _stitch_info.crop[i].left + _stitch_info.crop[i].right;
409        }
410        _fisheye[0].width = fisheye_width_sum / _fisheye_num;
411        _fisheye[0].width = XCAM_ALIGN_UP (_fisheye[0].width, 16);
412        _fisheye[0].height = out_info.height + _stitch_info.crop[0].top + _stitch_info.crop[0].bottom;
413        XCAM_LOG_INFO (
414            "fisheye correction output size width:%d height:%d",
415            _fisheye[0].width, _fisheye[0].height);
416
417        for (int i = 1; i < _fisheye_num; i++) {
418            _fisheye[i].width = _fisheye[0].width;
419            _fisheye[i].height = _fisheye[0].height;
420        }
421
422        float max_dst_longitude, max_dst_latitude;
423        for (int i = 0; i < _fisheye_num; ++i) {
424            max_dst_latitude = (_stitch_info.fisheye_info[i].wide_angle > 180.0f) ?
425                               180.0f : _stitch_info.fisheye_info[i].wide_angle;
426            max_dst_longitude = max_dst_latitude * _fisheye[i].width / _fisheye[i].height;
427
428            _fisheye[i].handler->set_dst_range (max_dst_longitude, max_dst_latitude);
429            _fisheye[i].handler->set_output_size (_fisheye[i].width, _fisheye[i].height);
430        }
431    } else {
432        _fisheye[0].height = out_info.height + _stitch_info.crop[0].top + _stitch_info.crop[0].bottom;
433
434        float view_angle[XCAM_STITCH_FISHEYE_MAX_NUM];
435
436        view_angle[0] = 68.0f;
437        _fisheye[0].width = view_angle[0] / 360.0f * out_info.width;
438        _fisheye[0].width = XCAM_ALIGN_UP (_fisheye[0].width, 32);
439
440        view_angle[1] = 152.0f;
441        _fisheye[1].width = view_angle[1] / 360.0f * out_info.width;
442        _fisheye[1].width = XCAM_ALIGN_UP (_fisheye[1].width, 32);
443
444        view_angle[2] = 68.0f;
445        _fisheye[2].width = view_angle[2] / 360.0f * out_info.width;
446        _fisheye[2].width = XCAM_ALIGN_UP (_fisheye[2].width, 32);
447
448        view_angle[3] = 152.0f;
449        _fisheye[3].width = view_angle[3] / 360.0f * out_info.width;
450        _fisheye[3].width = XCAM_ALIGN_UP (_fisheye[3].width, 32);
451
452        XCAM_LOG_INFO (
453            "fisheye correction output size width:%d height:%d",
454            _fisheye[0].width, _fisheye[0].height);
455
456        BowlDataConfig bowl_data_config[XCAM_STITCH_FISHEYE_MAX_NUM];
457
458        bowl_data_config[0].angle_start = -view_angle[0] / 2;
459        bowl_data_config[0].angle_end = view_angle[0] / 2;
460
461        for (int i = 1; i < _fisheye_num; i++) {
462            _fisheye[i].height = _fisheye[0].height;
463            float angle_center = 360.0f / _fisheye_num * i;
464            bowl_data_config[i].angle_start = angle_center - view_angle[i] / 2;
465            bowl_data_config[i].angle_end = angle_center + view_angle[i] / 2;
466        }
467
468        for(int i = 0; i < _fisheye_num; i++) {
469            _fisheye[i].handler->set_bowl_config(bowl_data_config[i]);
470            _fisheye[i].handler->set_output_size (_fisheye[i].width, _fisheye[i].height);
471        }
472
473        for(int i = 0; i < _fisheye_num; i++) {
474            _stitch_info.merge_width[i] = XCAM_ALIGN_UP((uint32_t)(20.0f / 360.0f * out_info.width), 32);
475        }
476    }
477}
478
479void
480CLImage360Stitch::update_image_overlap ()
481{
482    static bool is_merge_info_inited = false;
483    if (!is_merge_info_inited) {
484        int idx_next = 1;
485        for (int i = 0; i < _fisheye_num; i++) {
486            idx_next = (i == (_fisheye_num - 1)) ? 0 : (i + 1);
487
488            _img_merge_info[i].left.pos_x = _stitch_info.crop[i].left;
489            _img_merge_info[i].left.pos_y = _stitch_info.crop[i].top;
490            _img_merge_info[i].left.width = _stitch_info.merge_width[i];
491            _img_merge_info[i].left.height = _fisheye[i].height - _stitch_info.crop[i].top
492                                             - _stitch_info.crop[i].bottom;
493
494            _img_merge_info[i].right.pos_x = _fisheye[i].width - _stitch_info.crop[i].right
495                                             - _stitch_info.merge_width[idx_next];
496            _img_merge_info[i].right.pos_y = _stitch_info.crop[i].top;
497            _img_merge_info[i].right.width = _stitch_info.merge_width[idx_next];
498            _img_merge_info[i].right.height = _fisheye[i].height - _stitch_info.crop[i].top
499                                              - _stitch_info.crop[i].bottom;
500        }
501
502        is_merge_info_inited = true;
503    }
504
505    for (int i = 0; i < _fisheye_num; i++) {
506        set_image_overlap (i, _img_merge_info[i].left, _img_merge_info[i].right);
507    }
508}
509
510XCamReturn
511CLImage360Stitch::prepare_buffer_pool_video_info (
512    const VideoBufferInfo &input, VideoBufferInfo &output)
513{
514    if (_output_width == 0 || _output_height == 0) {
515        XCAM_LOG_ERROR ("incorrect output size: width:%d height:%d", _output_width, _output_height);
516        return XCAM_RETURN_ERROR_PARAM;
517    }
518
519    // aligned at least XCAM_CL_BLENDER_ALIGNMENT_X
520    uint32_t aligned_width = XCAM_MAX (16, XCAM_CL_BLENDER_ALIGNMENT_X);
521    output.init (
522        input.format, _output_width, _output_height,
523        XCAM_ALIGN_UP(_output_width, aligned_width), XCAM_ALIGN_UP(_output_height, 16));
524
525    return XCAM_RETURN_NO_ERROR;
526}
527
528XCamReturn
529CLImage360Stitch::ensure_fisheye_parameters (
530    SmartPtr<VideoBuffer> &input, SmartPtr<VideoBuffer> &output)
531{
532    static bool is_fisheye_inited = false;
533
534    if (!is_fisheye_inited) {
535        calc_fisheye_initial_info (output);
536        is_fisheye_inited = true;
537    }
538
539    SmartPtr<VideoBuffer> pre_buf;
540    SmartPtr<VideoBuffer> cur_buf = input;
541    for (int i = 0; i < _fisheye_num; i++) {
542        if (!_fisheye[i].pool.ptr ())
543            create_buffer_pool (_fisheye[i].pool, _fisheye[i].width, _fisheye[i].height);
544
545        _fisheye[i].buf = _fisheye[i].pool->get_buffer (_fisheye[i].pool);
546        XCAM_ASSERT (_fisheye[i].buf.ptr ());
547
548        XCamReturn ret = ensure_handler_parameters (_fisheye[i].handler, cur_buf, _fisheye[i].buf);
549        STITCH_CHECK (ret, "execute fisheye prepare_parameters failed");
550
551        if (!_all_in_one_img) {
552            pre_buf = cur_buf;
553            cur_buf = cur_buf->find_typed_attach<VideoBuffer> ();
554            if (!cur_buf.ptr () && (i != (_fisheye_num - 1))) {
555                XCAM_LOG_ERROR ("conflicting attached buffers and fisheye number");
556                return XCAM_RETURN_ERROR_PARAM;
557            }
558            pre_buf->detach_buffer (cur_buf);
559        }
560    }
561
562    return XCAM_RETURN_NO_ERROR;
563}
564
565XCamReturn
566CLImage360Stitch::prepare_global_scale_blender_parameters (
567    SmartPtr<VideoBuffer> &input0, SmartPtr<VideoBuffer> &input1, SmartPtr<VideoBuffer> &output,
568    int idx, int idx_next, int &cur_start_pos)
569{
570    const VideoBufferInfo &in0_info = input0->get_video_info ();
571    const VideoBufferInfo &in1_info = input1->get_video_info ();
572    const VideoBufferInfo &out_info = output->get_video_info ();
573
574    XCAM_ASSERT (in0_info.height == in1_info.height);
575    XCAM_ASSERT (in0_info.width <= out_info.width && in1_info.width <= out_info.width);
576
577    Rect left_lap = get_image_overlap (idx, 1);
578    Rect right_lap = get_image_overlap (idx_next, 0);
579
580    int left_img_mid = XCAM_ALIGN_DOWN (in0_info.width / 2, XCAM_CL_BLENDER_ALIGNMENT_X);
581    int right_img_mid = XCAM_ALIGN_DOWN (in1_info.width / 2, XCAM_CL_BLENDER_ALIGNMENT_X);
582
583    int32_t prev_pos;
584    prev_pos = left_lap.pos_x;
585    left_lap.pos_x = XCAM_ALIGN_AROUND (left_lap.pos_x, XCAM_CL_BLENDER_ALIGNMENT_X);
586    left_lap.width = XCAM_ALIGN_UP (left_lap.width, XCAM_CL_BLENDER_ALIGNMENT_X);
587    right_lap.pos_x += left_lap.pos_x - prev_pos;
588    right_lap.pos_x = XCAM_ALIGN_AROUND (right_lap.pos_x, XCAM_CL_BLENDER_ALIGNMENT_X);
589    right_lap.width = left_lap.width;
590
591    Rect area;
592    area.pos_y = left_lap.pos_y;
593    area.height = left_lap.height;
594    area.pos_x = left_img_mid;
595    area.width = left_lap.pos_x + left_lap.width - left_img_mid;
596    _blender[idx]->set_input_valid_area (area, 0);
597
598    area.pos_y = right_lap.pos_y;
599    area.height = right_lap.height;
600    area.pos_x = right_lap.pos_x;
601    area.width = right_img_mid - right_lap.pos_x;
602    _blender[idx]->set_input_valid_area (area, 1);
603
604    Rect out_merge_window;
605    out_merge_window.width = left_lap.width;
606    out_merge_window.pos_x = cur_start_pos + (left_lap.pos_x - left_img_mid);
607    out_merge_window.pos_y = 0;
608    out_merge_window.height = out_info.height;
609    _blender[idx]->set_merge_window (out_merge_window);
610
611    _blender[idx]->set_input_merge_area (left_lap, 0);
612    _blender[idx]->set_input_merge_area (right_lap, 1);
613
614    cur_start_pos += left_lap.pos_x - left_img_mid + right_img_mid - right_lap.pos_x;
615    return XCAM_RETURN_NO_ERROR;
616}
617
618XCamReturn
619CLImage360Stitch::prepare_local_scale_blender_parameters (
620    SmartPtr<VideoBuffer> &input0, SmartPtr<VideoBuffer> &input1, SmartPtr<VideoBuffer> &output, int idx, int idx_next)
621{
622    const VideoBufferInfo &in0_info = input0->get_video_info ();
623    const VideoBufferInfo &in1_info = input1->get_video_info ();
624    const VideoBufferInfo &out_info = output->get_video_info ();
625
626    XCAM_ASSERT (in0_info.height == in1_info.height);
627    XCAM_ASSERT (in0_info.width <= out_info.width && in1_info.width <= out_info.width);
628
629    Rect left_lap = get_image_overlap (idx, 1);
630    Rect right_lap = get_image_overlap (idx_next, 0);
631
632    int left_img_mid = XCAM_ALIGN_DOWN (in0_info.width / 2, XCAM_CL_BLENDER_ALIGNMENT_X);
633    int right_img_mid = XCAM_ALIGN_DOWN (in1_info.width / 2, XCAM_CL_BLENDER_ALIGNMENT_X);
634    int cur_start_pos = XCAM_ALIGN_DOWN (out_info.width / _fisheye_num * idx, XCAM_CL_BLENDER_ALIGNMENT_X);
635    int merge_std_width = XCAM_ALIGN_DOWN (out_info.width / _fisheye_num, XCAM_CL_BLENDER_ALIGNMENT_X);
636
637    int32_t prev_pos;
638    prev_pos = left_lap.pos_x;
639    left_lap.pos_x = XCAM_ALIGN_AROUND (left_lap.pos_x, XCAM_CL_BLENDER_ALIGNMENT_X);
640    left_lap.width = XCAM_ALIGN_UP (left_lap.width, XCAM_CL_BLENDER_ALIGNMENT_X);
641    right_lap.pos_x += left_lap.pos_x - prev_pos;
642    right_lap.pos_x = XCAM_ALIGN_AROUND (right_lap.pos_x, XCAM_CL_BLENDER_ALIGNMENT_X);
643    right_lap.width = left_lap.width;
644
645    Rect area;
646    area.pos_y = left_lap.pos_y;
647    area.height = left_lap.height;
648    area.pos_x = left_img_mid;
649    area.width = left_lap.pos_x + left_lap.width - left_img_mid;
650    _blender[idx]->set_input_valid_area (area, 0);
651
652    area.pos_y = right_lap.pos_y;
653    area.height = right_lap.height;
654    area.pos_x = right_lap.pos_x;
655    area.width = right_img_mid - right_lap.pos_x;
656    _blender[idx]->set_input_valid_area (area, 1);
657
658    Rect out_merge_window;
659    int delta_width = merge_std_width - (right_img_mid - right_lap.pos_x) - (left_lap.pos_x - left_img_mid);
660    out_merge_window.width = left_lap.width + delta_width;
661    out_merge_window.pos_x = cur_start_pos + (left_lap.pos_x - left_img_mid);
662    out_merge_window.pos_y = 0;
663    out_merge_window.height = out_info.height;
664    _blender[idx]->set_merge_window (out_merge_window);
665
666    _blender[idx]->set_input_merge_area (left_lap, 0);
667    _blender[idx]->set_input_merge_area (right_lap, 1);
668
669    return XCAM_RETURN_NO_ERROR;
670}
671
672bool
673CLImage360Stitch::create_buffer_pool (SmartPtr<BufferPool> &buf_pool, uint32_t width, uint32_t height)
674{
675    VideoBufferInfo buf_info;
676    width = XCAM_ALIGN_UP (width, 16);
677    buf_info.init (V4L2_PIX_FMT_NV12, width, height,
678                   XCAM_ALIGN_UP (width, 16), XCAM_ALIGN_UP (height, 16));
679
680    buf_pool = new CLVideoBufferPool ();
681    XCAM_ASSERT (buf_pool.ptr ());
682    buf_pool->set_video_info (buf_info);
683    if (!buf_pool->reserve (6)) {
684        XCAM_LOG_ERROR ("CLImage360Stitch init buffer pool failed");
685        return false;
686    }
687
688    return true;
689}
690
691XCamReturn
692CLImage360Stitch::reset_buffer_info (SmartPtr<VideoBuffer> &input)
693{
694    VideoBufferInfo reset_info;
695    const VideoBufferInfo &buf_info = input->get_video_info ();
696
697    uint32_t reset_width = 0;
698    for (int i = 0; i < _fisheye_num; i++) {
699        Rect img_left = get_image_overlap (i, 0);
700        Rect img_right = get_image_overlap (i, 1);
701
702        reset_width += img_right.pos_x - img_left.pos_x;
703    }
704
705    reset_width = XCAM_ALIGN_UP (reset_width, XCAM_CL_BLENDER_ALIGNMENT_X);
706    reset_info.init (buf_info.format, reset_width, buf_info.height,
707                     buf_info.aligned_width, buf_info.aligned_height);
708
709    input->set_video_info (reset_info);
710    return XCAM_RETURN_NO_ERROR;
711}
712
713XCamReturn
714CLImage360Stitch::prepare_parameters (SmartPtr<VideoBuffer> &input, SmartPtr<VideoBuffer> &output)
715{
716    XCamReturn ret = XCAM_RETURN_NO_ERROR;
717    if (!_is_stitch_inited)
718        set_stitch_info (get_default_stitch_info (_res_mode));
719
720    ret = ensure_fisheye_parameters (input, output);
721    STITCH_CHECK (ret, "ensure fisheye parameters failed");
722
723    update_image_overlap ();
724    if (_scale_mode == CLBlenderScaleLocal) {
725        int idx_next = 1;
726        for (int i = 0; i < _fisheye_num; i++) {
727            idx_next = (i == (_fisheye_num - 1)) ? 0 : (i + 1);
728
729            ret = prepare_local_scale_blender_parameters (
730                      _fisheye[i].buf, _fisheye[idx_next].buf, output, i, idx_next);
731            STITCH_CHECK (ret, "prepare local scale blender parameters failed");
732
733            _fisheye[i].buf->attach_buffer (_fisheye[idx_next].buf);
734            ret = ensure_handler_parameters (_blender[i], _fisheye[i].buf, output);
735            STITCH_CHECK (ret, "blender: execute ensure_parameters failed");
736            _fisheye[i].buf->detach_buffer (_fisheye[idx_next].buf);
737        }
738    } else { //global scale
739        const VideoBufferInfo &buf_info = output->get_video_info ();
740        if (!_scale_buf_pool.ptr ())
741            create_buffer_pool (_scale_buf_pool, buf_info.width + XCAM_BLENDER_GLOBAL_SCALE_EXT_WIDTH, buf_info.height);
742        SmartPtr<VideoBuffer> scale_input = _scale_buf_pool->get_buffer (_scale_buf_pool);
743        XCAM_ASSERT (scale_input.ptr ());
744
745        int idx_next = 1;
746        int cur_start_pos = 0;
747        for (int i = 0; i < _fisheye_num; i++) {
748            idx_next = (i == (_fisheye_num - 1)) ? 0 : (i + 1);
749
750            ret = prepare_global_scale_blender_parameters (
751                      _fisheye[i].buf, _fisheye[idx_next].buf, scale_input, i, idx_next, cur_start_pos);
752            STITCH_CHECK (ret, "prepare global scale blender parameters failed");
753
754            _fisheye[i].buf->attach_buffer (_fisheye[idx_next].buf);
755            ret = ensure_handler_parameters (_blender[i], _fisheye[i].buf, scale_input);
756            STITCH_CHECK (ret, "blender: execute ensure_parameters failed");
757            _fisheye[i].buf->detach_buffer (_fisheye[idx_next].buf);
758        }
759
760        reset_buffer_info (scale_input);
761        _scale_global_input = scale_input;
762        _scale_global_output = output;
763    }
764
765    return XCAM_RETURN_NO_ERROR;
766}
767
768XCamReturn
769CLImage360Stitch::execute_done (SmartPtr<VideoBuffer> &output)
770{
771#if HAVE_OPENCV
772    for (int i = 0; i < _fisheye_num; i++) {
773        if (!_feature_match[i]->is_ocl_path ()) {
774            get_context ()->finish ();
775            break;
776        }
777    }
778#endif
779
780    _scale_global_input.release ();
781    _scale_global_output.release ();
782
783    return CLMultiImageHandler::execute_done (output);
784}
785
786static void
787convert_to_stitch_rect (Rect xcam_rect, Rect &stitch_rect)
788{
789    stitch_rect.pos_x = xcam_rect.pos_x;
790    stitch_rect.pos_y = xcam_rect.pos_y + xcam_rect.height / 3;
791    stitch_rect.width = xcam_rect.width;
792    stitch_rect.height = xcam_rect.height / 3;
793}
794
795static void
796convert_to_xcam_rect (Rect stitch_rect, Rect &xcam_rect)
797{
798    xcam_rect.pos_x = stitch_rect.pos_x;
799    xcam_rect.width = stitch_rect.width;
800}
801
802
803XCamReturn
804CLImage360Stitch::sub_handler_execute_done (SmartPtr<CLImageHandler> &handler)
805{
806#if HAVE_OPENCV
807    XCAM_ASSERT (handler.ptr ());
808
809    if (handler.ptr () == _fisheye[_fisheye_num - 1].handler.ptr ()) {
810        int idx_next = 1;
811        Rect crop_left, crop_right;
812
813        for (int i = 0; i < _fisheye_num; i++) {
814            idx_next = (i == (_fisheye_num - 1)) ? 0 : (i + 1);
815
816            convert_to_stitch_rect (_img_merge_info[i].right, crop_left);
817            convert_to_stitch_rect (_img_merge_info[idx_next].left, crop_right);
818
819            _feature_match[i]->optical_flow_feature_match (
820                _fisheye[i].buf, _fisheye[idx_next].buf, crop_left, crop_right, _fisheye[i].width);
821
822            convert_to_xcam_rect (crop_left, _img_merge_info[i].right);
823            convert_to_xcam_rect (crop_right, _img_merge_info[idx_next].left);
824        }
825    }
826#else
827    XCAM_UNUSED (handler);
828#endif
829
830    return XCAM_RETURN_NO_ERROR;
831}
832
833static SmartPtr<CLImageKernel>
834create_blender_global_scale_kernel (
835    const SmartPtr<CLContext> &context,
836    SmartPtr<CLImage360Stitch> &stitch,
837    bool is_uv)
838{
839    char transform_option[1024];
840    snprintf (transform_option, sizeof(transform_option), "-DPYRAMID_UV=%d", is_uv ? 1 : 0);
841
842    static const XCamKernelInfo &kernel_info = {
843        "kernel_pyramid_scale",
844#include "kernel_gauss_lap_pyramid.clx"
845        , 0
846    };
847
848    SmartPtr<CLImageKernel> kernel;
849    kernel = new CLBlenderGlobalScaleKernel (context, stitch, is_uv);
850    XCAM_ASSERT (kernel.ptr ());
851    XCAM_FAIL_RETURN (
852        ERROR,
853        kernel->build_kernel (kernel_info, transform_option) == XCAM_RETURN_NO_ERROR,
854        NULL,
855        "load blender global scaling kernel(%s) failed", is_uv ? "UV" : "Y");
856
857    return kernel;
858}
859
860SmartPtr<CLImageHandler>
861create_image_360_stitch (
862    const SmartPtr<CLContext> &context, bool need_seam,
863    CLBlenderScaleMode scale_mode, bool fisheye_map, bool need_lsc, SurroundMode surround_mode,
864    StitchResMode res_mode, int fisheye_num, bool all_in_one_img)
865{
866    const int layer = 2;
867    const bool need_uv = true;
868    SmartPtr<CLFisheyeHandler> fisheye;
869    SmartPtr<CLBlender> blender;
870    SmartPtr<CLImage360Stitch> stitch = new CLImage360Stitch (
871        context, scale_mode, surround_mode, res_mode, fisheye_num, all_in_one_img);
872    XCAM_ASSERT (stitch.ptr ());
873
874    for (int index = 0; index < fisheye_num; ++index) {
875        fisheye = create_fisheye_handler (context, surround_mode, fisheye_map, need_lsc).dynamic_cast_ptr<CLFisheyeHandler> ();
876        XCAM_FAIL_RETURN (ERROR, fisheye.ptr (), NULL, "image_360_stitch create fisheye handler failed");
877        fisheye->disable_buf_pool (true);
878        stitch->set_fisheye_handler (fisheye, index);
879    }
880
881    for (int index = 0; index < fisheye_num; ++index) {
882        blender = create_pyramid_blender (context, layer, need_uv, need_seam, scale_mode).dynamic_cast_ptr<CLBlender> ();
883        XCAM_FAIL_RETURN (ERROR, blender.ptr (), NULL, "image_360_stitch create blender failed");
884        blender->disable_buf_pool (true);
885        stitch->set_blender (blender, index);
886    }
887
888    if (scale_mode == CLBlenderScaleGlobal) {
889        int max_plane = need_uv ? 2 : 1;
890        bool uv_status[2] = {false, true};
891        for (int plane = 0; plane < max_plane; ++plane) {
892            SmartPtr<CLImageKernel> kernel = create_blender_global_scale_kernel (context, stitch, uv_status[plane]);
893            XCAM_FAIL_RETURN (ERROR, kernel.ptr (), NULL, "create blender global scaling kernel failed");
894            stitch->add_kernel (kernel);
895        }
896    }
897
898    return stitch;
899}
900
901}
902
903