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 ¤t, 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 ¤t = 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