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 (at) 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 35 namespace XCam { 36 37 CLBlenderGlobalScaleKernel::CLBlenderGlobalScaleKernel ( 38 const SmartPtr<CLContext> &context, SmartPtr<CLImage360Stitch> &stitch, bool is_uv) 39 : CLBlenderScaleKernel (context, is_uv) 40 , _stitch (stitch) 41 { 42 } 43 44 SmartPtr<CLImage> 45 CLBlenderGlobalScaleKernel::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 71 SmartPtr<CLImage> 72 CLBlenderGlobalScaleKernel::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 97 bool 98 CLBlenderGlobalScaleKernel::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 112 static CVFMConfig 113 get_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 163 static StitchInfo 164 get_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 273 CLImage360Stitch::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 297 bool 298 CLImage360Stitch::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 315 StitchInfo 316 CLImage360Stitch::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 326 bool 327 CLImage360Stitch::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 336 bool 337 CLImage360Stitch::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 345 void 346 CLImage360Stitch::set_fisheye_intrinsic (IntrinsicParameter intrinsic_param, int index) 347 { 348 _fisheye[index].handler->set_intrinsic_param(intrinsic_param); 349 } 350 351 void 352 CLImage360Stitch::set_fisheye_extrinsic (ExtrinsicParameter extrinsic_param, int index) 353 { 354 _fisheye[index].handler->set_extrinsic_param(extrinsic_param); 355 } 356 357 const BowlDataConfig & 358 CLImage360Stitch::get_fisheye_bowl_config (int index) 359 { 360 XCAM_ASSERT (index < _fisheye_num); 361 return _fisheye[index].handler->get_bowl_config (); 362 } 363 364 bool 365 CLImage360Stitch::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 373 void 374 CLImage360Stitch::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 387 void 388 CLImage360Stitch::set_feature_match_config (const int idx, CVFMConfig config) 389 { 390 _feature_match[idx]->set_config (config); 391 } 392 393 CVFMConfig 394 CLImage360Stitch::get_feature_match_config (const int idx) 395 { 396 return _feature_match[idx]->get_config (); 397 } 398 #endif 399 400 void 401 CLImage360Stitch::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 479 void 480 CLImage360Stitch::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 510 XCamReturn 511 CLImage360Stitch::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 528 XCamReturn 529 CLImage360Stitch::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 565 XCamReturn 566 CLImage360Stitch::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 618 XCamReturn 619 CLImage360Stitch::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 672 bool 673 CLImage360Stitch::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 691 XCamReturn 692 CLImage360Stitch::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 713 XCamReturn 714 CLImage360Stitch::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 768 XCamReturn 769 CLImage360Stitch::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 786 static void 787 convert_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 795 static void 796 convert_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 803 XCamReturn 804 CLImage360Stitch::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 833 static SmartPtr<CLImageKernel> 834 create_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 860 SmartPtr<CLImageHandler> 861 create_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