1 /*
2  * test-soft-image.cpp - test soft image
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  */
20 
21 #include "test_common.h"
22 #include "test_inline.h"
23 #include <buffer_pool.h>
24 #include <image_handler.h>
25 #include <image_file_handle.h>
26 #include <soft/soft_video_buf_allocator.h>
27 #include <interface/blender.h>
28 #include <interface/geo_mapper.h>
29 #include <interface/stitcher.h>
30 #include <calibration_parser.h>
31 #include <string>
32 
33 #if (!defined(ANDROID) && (HAVE_OPENCV))
34 #include <ocl/cv_base_class.h>
35 #endif
36 
37 #define XCAM_TEST_SOFT_IMAGE_DEBUG 0
38 
39 #if (!defined(ANDROID) && (HAVE_OPENCV))
40 #define XCAM_TEST_OPENCV 1
41 #else
42 #define XCAM_TEST_OPENCV 0
43 #endif
44 
45 #define XCAM_TEST_MAX_STR_SIZE 1024
46 
47 #define FISHEYE_CONFIG_PATH "./"
48 
49 #define MAP_WIDTH 3
50 #define MAP_HEIGHT 4
51 
52 static PointFloat2 map_table[MAP_HEIGHT * MAP_WIDTH] = {
53     {160.0f, 120.0f}, {480.0f, 120.0f}, {796.0f, 120.0f},
54     {60.0f, 240.0f}, {480.0f, 240.0f}, {900.0f, 240.0f},
55     {16.0f, 360.0f}, {480.0f, 360.0f}, {944.0f, 360.0f},
56     {0.0f, 480.0f}, {480.0f, 480.0f}, {960.0f, 480.0f},
57 };
58 
59 using namespace XCam;
60 
61 enum SoftType {
62     SoftTypeNone     = 0,
63     SoftTypeBlender,
64     SoftTypeRemap,
65     SoftTypeStitch,
66 };
67 
68 #define RUN_N(statement, loop, msg, ...) \
69     for (int i = 0; i < loop; ++i) {                          \
70         CHECK (statement, msg, ## __VA_ARGS__);               \
71         FPS_CALCULATION (soft-image, XCAM_OBJ_DUR_FRAME_NUM); \
72     }
73 
74 #define ADD_ENELEMT(elements, file_name) \
75     {                                                                \
76         SmartPtr<SoftElement> element = new SoftElement (file_name); \
77         elements.push_back (element);                                \
78     }
79 
80 #if XCAM_TEST_OPENCV
81 const static cv::Scalar color = cv::Scalar (0, 0, 255);
82 const static int fontFace = cv::FONT_HERSHEY_COMPLEX;
83 #endif
84 
85 class SoftElement {
86 public:
87     explicit SoftElement (const char *file_name = NULL, uint32_t width = 0, uint32_t height = 0);
88     ~SoftElement ();
89 
90     void set_buf_size (uint32_t width, uint32_t height);
get_width() const91     uint32_t get_width () const {
92         return _width;
93     }
get_height() const94     uint32_t get_height () const {
95         return _height;
96     }
97 
get_file_name() const98     const char *get_file_name () const {
99         return _file_name;
100     }
101 
get_buf()102     SmartPtr<VideoBuffer> &get_buf () {
103         return _buf;
104     }
105 
106     XCamReturn open_file (const char *option);
107     XCamReturn close_file ();
108     XCamReturn rewind_file ();
109 
110     XCamReturn read_buf ();
111     XCamReturn write_buf ();
112 
113     XCamReturn create_buf_pool (const VideoBufferInfo &info, uint32_t count);
114 
115 #if XCAM_TEST_OPENCV
116     XCamReturn cv_open_writer ();
117     void cv_write_image (char *img_name, char *frame_str, char *idx_str = NULL);
118 #endif
119 
120 private:
121     char                 *_file_name;
122     uint32_t              _width;
123     uint32_t              _height;
124     SmartPtr<VideoBuffer> _buf;
125 
126     ImageFileHandle       _file;
127     SmartPtr<BufferPool>  _pool;
128 #if XCAM_TEST_OPENCV
129     cv::VideoWriter       _writer;
130 #endif
131 };
132 
133 typedef std::vector<SmartPtr<SoftElement>> SoftElements;
134 
SoftElement(const char * file_name,uint32_t width,uint32_t height)135 SoftElement::SoftElement (const char *file_name, uint32_t width, uint32_t height)
136     : _file_name (NULL)
137     , _width (width)
138     , _height (height)
139 {
140     if (file_name)
141         _file_name = strndup (file_name, XCAM_TEST_MAX_STR_SIZE);
142 }
143 
~SoftElement()144 SoftElement::~SoftElement ()
145 {
146     _file.close ();
147 
148     if (_file_name)
149         xcam_free (_file_name);
150 }
151 
152 void
set_buf_size(uint32_t width,uint32_t height)153 SoftElement::set_buf_size (uint32_t width, uint32_t height)
154 {
155     _width = width;
156     _height = height;
157 }
158 
159 XCamReturn
open_file(const char * option)160 SoftElement::open_file (const char *option)
161 {
162     if (_file.open (_file_name, option) != XCAM_RETURN_NO_ERROR) {
163         XCAM_LOG_ERROR ("open %s failed.", _file_name);
164         return XCAM_RETURN_ERROR_FILE;
165     }
166 
167     return XCAM_RETURN_NO_ERROR;
168 }
169 
170 XCamReturn
close_file()171 SoftElement::close_file ()
172 {
173     return _file.close ();
174 }
175 
176 XCamReturn
rewind_file()177 SoftElement::rewind_file ()
178 {
179     return _file.rewind ();
180 }
181 
182 XCamReturn
create_buf_pool(const VideoBufferInfo & info,uint32_t count)183 SoftElement::create_buf_pool (const VideoBufferInfo &info, uint32_t count)
184 {
185     _pool = new SoftVideoBufAllocator ();
186     _pool->set_video_info (info);
187     if (!_pool->reserve (count)) {
188         XCAM_LOG_ERROR ("create buffer pool failed");
189         return XCAM_RETURN_ERROR_MEM;
190     }
191 
192     return XCAM_RETURN_NO_ERROR;
193 }
194 
195 XCamReturn
read_buf()196 SoftElement::read_buf ()
197 {
198     _buf = _pool->get_buffer (_pool);
199     XCAM_ASSERT (_buf.ptr ());
200 
201     return _file.read_buf (_buf);
202 }
203 
204 XCamReturn
write_buf()205 SoftElement::write_buf () {
206     return _file.write_buf (_buf);
207 }
208 
209 #if XCAM_TEST_OPENCV
210 XCamReturn
cv_open_writer()211 SoftElement::cv_open_writer ()
212 {
213     XCAM_FAIL_RETURN (
214         ERROR,
215         _width && _height,
216         XCAM_RETURN_ERROR_PARAM,
217         "invalid size width:%d height:%d", _width, _height);
218 
219     cv::Size frame_size = cv::Size (_width, _height);
220     if (!_writer.open (_file_name, CV_FOURCC('X', '2', '6', '4'), 30, frame_size)) {
221         XCAM_LOG_ERROR ("open file %s failed", _file_name);
222         return XCAM_RETURN_ERROR_FILE;
223     }
224 
225     return XCAM_RETURN_NO_ERROR;
226 }
227 
228 void
cv_write_image(char * img_name,char * frame_str,char * idx_str)229 SoftElement::cv_write_image (char *img_name, char *frame_str, char *idx_str)
230 {
231     cv::Mat mat;
232 
233 #if XCAM_TEST_SOFT_IMAGE_DEBUG
234     convert_to_mat (_buf, mat);
235 
236     cv::putText (mat, frame_str, cv::Point(20, 50), fontFace, 2.0, color, 2, 8, false);
237     if(idx_str)
238         cv::putText (mat, idx_str, cv::Point(20, 110), fontFace, 2.0, color, 2, 8, false);
239 
240     cv::imwrite (img_name, mat);
241 #else
242     XCAM_UNUSED (img_name);
243     XCAM_UNUSED (frame_str);
244     XCAM_UNUSED (idx_str);
245 #endif
246 
247     if (_writer.isOpened ()) {
248         if (mat.empty())
249             convert_to_mat (_buf, mat);
250 
251         _writer.write (mat);
252     }
253 }
254 #endif
255 
256 static int
parse_camera_info(const char * path,uint32_t idx,CameraInfo & info,uint32_t camera_count)257 parse_camera_info (const char *path, uint32_t idx, CameraInfo &info, uint32_t camera_count)
258 {
259     static const char *instrinsic_names[] = {
260         "intrinsic_camera_front.txt", "intrinsic_camera_right.txt",
261         "intrinsic_camera_rear.txt", "intrinsic_camera_left.txt"
262     };
263     static const char *exstrinsic_names[] = {
264         "extrinsic_camera_front.txt", "extrinsic_camera_right.txt",
265         "extrinsic_camera_rear.txt", "extrinsic_camera_left.txt"
266     };
267     static const float viewpoints_range[] = {64.0f, 160.0f, 64.0f, 160.0f};
268 
269     char intrinsic_path[XCAM_TEST_MAX_STR_SIZE] = {'\0'};
270     char extrinsic_path[XCAM_TEST_MAX_STR_SIZE] = {'\0'};
271     snprintf (intrinsic_path, XCAM_TEST_MAX_STR_SIZE, "%s/%s", path, instrinsic_names[idx]);
272     snprintf (extrinsic_path, XCAM_TEST_MAX_STR_SIZE, "%s/%s", path, exstrinsic_names[idx]);
273 
274     CalibrationParser parser;
275     CHECK (
276         parser.parse_intrinsic_file (intrinsic_path, info.calibration.intrinsic),
277         "parse intrinsic params (%s)failed.", intrinsic_path);
278 
279     CHECK (
280         parser.parse_extrinsic_file (extrinsic_path, info.calibration.extrinsic),
281         "parse extrinsic params (%s)failed.", extrinsic_path);
282     info.calibration.extrinsic.trans_x += TEST_CAMERA_POSITION_OFFSET_X;
283 
284     info.angle_range = viewpoints_range[idx];
285     info.round_angle_start = (idx * 360.0f / camera_count) - info.angle_range / 2.0f;
286     return 0;
287 }
288 
289 static void
combine_name(const char * orig_name,const char * embedded_str,char * new_name)290 combine_name (const char *orig_name, const char *embedded_str, char *new_name)
291 {
292     const char *dir_delimiter = std::strrchr (orig_name, '/');
293 
294     if (dir_delimiter) {
295         std::string path (orig_name, dir_delimiter - orig_name + 1);
296         XCAM_ASSERT (path.c_str ());
297         snprintf (new_name, XCAM_TEST_MAX_STR_SIZE, "%s%s_%s", path.c_str (), embedded_str, dir_delimiter + 1);
298     } else {
299         snprintf (new_name, XCAM_TEST_MAX_STR_SIZE, "%s_%s", embedded_str, orig_name);
300     }
301 }
302 
303 static void
add_element(SoftElements & elements,const char * element_name,uint32_t width,uint32_t height)304 add_element (SoftElements &elements, const char *element_name, uint32_t width, uint32_t height)
305 {
306     char file_name[XCAM_TEST_MAX_STR_SIZE] = {'\0'};
307     combine_name (elements[0]->get_file_name (), element_name, file_name);
308 
309     SmartPtr<SoftElement> element = new SoftElement (file_name, width, height);
310     elements.push_back (element);
311 }
312 
313 static XCamReturn
elements_open_file(const SoftElements & elements,const char * option,const bool & nv12_output)314 elements_open_file (const SoftElements &elements, const char *option, const bool &nv12_output)
315 {
316     XCamReturn ret = XCAM_RETURN_NO_ERROR;
317 
318     for (uint32_t i = 0; i < elements.size (); ++i) {
319         if (nv12_output)
320             ret = elements[i]->open_file (option);
321 #if XCAM_TEST_OPENCV
322         else
323             ret = elements[i]->cv_open_writer ();
324 #endif
325 
326         if (ret != XCAM_RETURN_NO_ERROR) {
327             XCAM_LOG_ERROR ("open file(%s) failed", elements[i]->get_file_name ());
328             break;
329         }
330     }
331 
332     return ret;
333 }
334 
335 static XCamReturn
remap_topview_buf(BowlModel & model,const SmartPtr<VideoBuffer> & buf,SmartPtr<VideoBuffer> & topview_buf,uint32_t topview_width,uint32_t topview_height)336 remap_topview_buf (
337     BowlModel &model,
338     const SmartPtr<VideoBuffer> &buf,
339     SmartPtr<VideoBuffer> &topview_buf,
340     uint32_t topview_width, uint32_t topview_height)
341 {
342     BowlModel::PointMap points;
343 
344     uint32_t lut_w = topview_width / 4, lut_h = topview_height / 4;
345     float length_mm = 0.0f, width_mm = 0.0f;
346 
347     model.get_max_topview_area_mm (length_mm, width_mm);
348     XCAM_LOG_INFO ("Max Topview Area (L%.2fmm, W%.2fmm)", length_mm, width_mm);
349 
350     model.get_topview_rect_map (points, lut_w, lut_h);
351     SmartPtr<GeoMapper> mapper = GeoMapper::create_soft_geo_mapper ();
352     XCAM_ASSERT (mapper.ptr ());
353     mapper->set_output_size (topview_width, topview_height);
354     mapper->set_lookup_table (points.data (), lut_w, lut_h);
355 
356     XCamReturn ret = mapper->remap (buf, topview_buf);
357     if (ret != XCAM_RETURN_NO_ERROR) {
358         XCAM_LOG_ERROR ("remap stitched image to topview failed.");
359         return ret;
360     }
361 
362 #if 0
363     BowlModel::VertexMap bowl_vertices;
364     BowlModel::PointMap bowl_points;
365     uint32_t bowl_lut_w = 15, bowl_lut_h = 10;
366     model.get_bowlview_vertex_map (bowl_vertices, bowl_points, bowl_lut_w, bowl_lut_h);
367     for (uint32_t i = 0; i < bowl_lut_h; ++i) {
368         for (uint32_t j = 0; j < bowl_lut_w; ++j)
369         {
370             PointFloat3 &vetex = bowl_vertices[i * bowl_lut_w + j];
371             printf ("(%4.0f, %4.0f, %4.0f), ", vetex.x, vetex.y, vetex.z );
372         }
373         printf ("\n");
374     }
375 #endif
376 
377     return XCAM_RETURN_NO_ERROR;
378 }
379 
380 static void
write_image(const SoftElements & ins,const SoftElements & outs,const bool & nv12_output)381 write_image (const SoftElements &ins, const SoftElements &outs, const bool &nv12_output) {
382     if (nv12_output) {
383         for (uint32_t i = 0; i < outs.size (); ++i)
384             outs[i]->write_buf ();
385     }
386 #if XCAM_TEST_OPENCV
387     else {
388         static uint32_t frame_num = 0;
389         char img_name[XCAM_TEST_MAX_STR_SIZE] = {'\0'};
390         char frame_str[XCAM_TEST_MAX_STR_SIZE] = {'\0'};
391         std::snprintf (frame_str, XCAM_TEST_MAX_STR_SIZE, "frame:%d", frame_num);
392 
393         char idx_str[XCAM_TEST_MAX_STR_SIZE] = {'\0'};
394         for (uint32_t i = 0; i < ins.size (); ++i) {
395             std::snprintf (idx_str, XCAM_TEST_MAX_STR_SIZE, "idx:%d", i);
396             std::snprintf (img_name, XCAM_TEST_MAX_STR_SIZE, "orig_fisheye_%d_%d.jpg", frame_num, i);
397             ins[i]->cv_write_image (img_name, frame_str, idx_str);
398         }
399 
400         for (uint32_t i = 0; i < outs.size (); ++i) {
401             std::snprintf (img_name, XCAM_TEST_MAX_STR_SIZE, "%s_%d.jpg", outs[i]->get_file_name (), frame_num);
402             outs[i]->cv_write_image (img_name, frame_str);
403         }
404         frame_num++;
405     }
406 #endif
407 }
408 
409 static XCamReturn
ensure_output_format(const char * file_name,const SoftType & type,bool & nv12_output)410 ensure_output_format (const char *file_name, const SoftType &type, bool &nv12_output)
411 {
412     char suffix[XCAM_TEST_MAX_STR_SIZE] = {'\0'};
413     const char *ptr = std::strrchr (file_name, '.');
414     std::snprintf (suffix, XCAM_TEST_MAX_STR_SIZE, "%s", ptr + 1);
415     if (!strcasecmp (suffix, "mp4")) {
416 #if XCAM_TEST_OPENCV
417         if (type != SoftTypeStitch) {
418             XCAM_LOG_ERROR ("only stitch type supports MP4 output format");
419             return XCAM_RETURN_ERROR_PARAM;
420         }
421         nv12_output = false;
422 #else
423         XCAM_LOG_ERROR ("only supports NV12 output format");
424         return XCAM_RETURN_ERROR_PARAM;
425 #endif
426     }
427 
428     return XCAM_RETURN_NO_ERROR;
429 }
430 
431 static bool
check_element(const SoftElements & elements,const uint32_t & idx)432 check_element (const SoftElements &elements, const uint32_t &idx)
433 {
434     if (idx >= elements.size ())
435         return false;
436 
437     if (!elements[idx].ptr()) {
438         XCAM_LOG_ERROR ("SoftElement(idx:%d) ptr is NULL", idx);
439         return false;
440     }
441 
442     XCAM_FAIL_RETURN (
443         ERROR,
444         elements[idx]->get_width () && elements[idx]->get_height (),
445         false,
446         "SoftElement(idx:%d): invalid parameters width:%d height:%d",
447         idx, elements[idx]->get_width (), elements[idx]->get_height ());
448 
449     return true;
450 }
451 
452 static XCamReturn
check_elements(const SoftElements & elements)453 check_elements (const SoftElements &elements)
454 {
455     for (uint32_t i = 0; i < elements.size (); ++i) {
456         XCAM_FAIL_RETURN (
457             ERROR,
458             check_element (elements, i),
459             XCAM_RETURN_ERROR_PARAM,
460             "invalid SoftElement index:%d\n", i);
461     }
462 
463     return XCAM_RETURN_NO_ERROR;
464 }
465 
466 static XCamReturn
run_topview(const SmartPtr<Stitcher> & stitcher,const SoftElements & outs)467 run_topview (const SmartPtr<Stitcher> &stitcher, const SoftElements &outs)
468 {
469     BowlModel bowl_model (stitcher->get_bowl_config (), outs[0]->get_width (), outs[0]->get_height ());
470     return remap_topview_buf (bowl_model, outs[0]->get_buf (), outs[1]->get_buf (),
471                               outs[1]->get_width (), outs[1]->get_height ());
472 }
473 
474 static int
run_stitcher(const SmartPtr<Stitcher> & stitcher,const SoftElements & ins,const SoftElements & outs,bool nv12_output,bool save_output,int loop)475 run_stitcher (
476     const SmartPtr<Stitcher> &stitcher,
477     const SoftElements &ins, const SoftElements &outs,
478     bool nv12_output, bool save_output, int loop)
479 {
480     XCamReturn ret = XCAM_RETURN_NO_ERROR;
481     CHECK (check_elements (ins), "invalid input elements");
482     CHECK (check_elements (outs), "invalid output elements");
483 
484     VideoBufferList in_buffers;
485     while (loop--) {
486         for (uint32_t i = 0; i < ins.size (); ++i) {
487             CHECK (ins[i]->rewind_file (), "rewind buffer from file(%s) failed", ins[i]->get_file_name ());
488         }
489 
490         do {
491             in_buffers.clear ();
492 
493             for (uint32_t i = 0; i < ins.size (); ++i) {
494                 ret = ins[i]->read_buf();
495                 if (ret == XCAM_RETURN_BYPASS)
496                     break;
497                 CHECK (ret, "read buffer from file(%s) failed.", ins[i]->get_file_name ());
498 
499                 in_buffers.push_back (ins[i]->get_buf ());
500             }
501             if (ret == XCAM_RETURN_BYPASS)
502                 break;
503 
504             CHECK (
505                 stitcher->stitch_buffers (in_buffers, outs[0]->get_buf ()),
506                 "stitch buffer failed.");
507 
508             if (save_output) {
509                 if (check_element (outs, 1)) {
510                     CHECK (run_topview (stitcher, outs), "run topview failed");
511                 }
512 
513                 write_image (ins, outs, nv12_output);
514             }
515 
516             FPS_CALCULATION (soft - stitcher, XCAM_OBJ_DUR_FRAME_NUM);
517         } while (true);
518     }
519 
520     return 0;
521 }
522 
usage(const char * arg0)523 static void usage(const char* arg0)
524 {
525     printf ("Usage:\n"
526             "%s --type TYPE--input0 file0 --input1 file1 --output file\n"
527             "\t--type              processing type, selected from: blend, remap, stitch, ...\n"
528             "\t--                  [stitch]: read calibration files from exported path $FISHEYE_CONFIG_PATH\n"
529             "\t--input0            input image(NV12)\n"
530             "\t--input1            input image(NV12)\n"
531             "\t--input2            input image(NV12)\n"
532             "\t--input3            input image(NV12)\n"
533             "\t--output            output image(NV12)\n"
534             "\t--in-w              optional, input width, default: 1920\n"
535             "\t--in-h              optional, input height, default: 1080\n"
536             "\t--out-w             optional, output width, default: 1920\n"
537             "\t--out-h             optional, output height, default: 960\n"
538             "\t--topview-w         optional, output width, default: 1280\n"
539             "\t--topview-h         optional, output height, default: 720\n"
540             "\t--save              optional, save file or not, select from [true/false], default: true\n"
541             "\t--loop              optional, how many loops need to run, default: 1\n"
542             "\t--help              usage\n",
543             arg0);
544 }
545 
main(int argc,char * argv[])546 int main (int argc, char *argv[])
547 {
548     uint32_t input_width = 1920;
549     uint32_t input_height = 1080;
550     uint32_t output_width = 1920; //output_height * 2;
551     uint32_t output_height = 960; //960;
552     uint32_t topview_width = 1280;
553     uint32_t topview_height = 720;
554     SoftType type = SoftTypeNone;
555 
556     SoftElements ins;
557     SoftElements outs;
558 
559     int loop = 1;
560     bool save_output = true;
561     bool nv12_output = true;
562 
563     const struct option long_opts[] = {
564         {"type", required_argument, NULL, 't'},
565         {"input0", required_argument, NULL, 'i'},
566         {"input1", required_argument, NULL, 'j'},
567         {"input2", required_argument, NULL, 'k'},
568         {"input3", required_argument, NULL, 'l'},
569         {"output", required_argument, NULL, 'o'},
570         {"in-w", required_argument, NULL, 'w'},
571         {"in-h", required_argument, NULL, 'h'},
572         {"out-w", required_argument, NULL, 'W'},
573         {"out-h", required_argument, NULL, 'H'},
574         {"topview-w", required_argument, NULL, 'P'},
575         {"topview-h", required_argument, NULL, 'V'},
576         {"save", required_argument, NULL, 's'},
577         {"loop", required_argument, NULL, 'L'},
578         {"help", no_argument, NULL, 'e'},
579         {NULL, 0, NULL, 0},
580     };
581 
582     int opt = -1;
583     while ((opt = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
584         switch (opt) {
585         case 't':
586             XCAM_ASSERT (optarg);
587             if (!strcasecmp (optarg, "blend"))
588                 type = SoftTypeBlender;
589             else if (!strcasecmp (optarg, "remap"))
590                 type = SoftTypeRemap;
591             else if (!strcasecmp (optarg, "stitch"))
592                 type = SoftTypeStitch;
593             else {
594                 XCAM_LOG_ERROR ("unknown type:%s", optarg);
595                 usage (argv[0]);
596                 return -1;
597             }
598             break;
599 
600         case 'i':
601             XCAM_ASSERT (optarg);
602             ADD_ENELEMT(ins, optarg);
603             break;
604         case 'j':
605             XCAM_ASSERT (optarg);
606             ADD_ENELEMT(ins, optarg);
607             break;
608         case 'k':
609             XCAM_ASSERT (optarg);
610             ADD_ENELEMT(ins, optarg);
611             break;
612         case 'l':
613             XCAM_ASSERT (optarg);
614             ADD_ENELEMT(ins, optarg);
615             break;
616         case 'o':
617             XCAM_ASSERT (optarg);
618             ADD_ENELEMT(outs, optarg);
619             break;
620         case 'w':
621             input_width = atoi(optarg);
622             break;
623         case 'h':
624             input_height = atoi(optarg);
625             break;
626         case 'W':
627             output_width = atoi(optarg);
628             break;
629         case 'H':
630             output_height = atoi(optarg);
631             break;
632         case 'P':
633             topview_width = atoi(optarg);
634             break;
635         case 'V':
636             topview_height = atoi(optarg);
637             break;
638         case 's':
639             save_output = (strcasecmp (optarg, "false") == 0 ? false : true);
640             break;
641         case 'L':
642             loop = atoi(optarg);
643             break;
644         default:
645             XCAM_LOG_ERROR ("getopt_long return unknown value:%c", opt);
646             usage (argv[0]);
647             return -1;
648         }
649     }
650 
651     if (optind < argc || argc < 2) {
652         XCAM_LOG_ERROR ("unknown option %s", argv[optind]);
653         usage (argv[0]);
654         return -1;
655     }
656 
657     if (SoftTypeNone == type) {
658         XCAM_LOG_ERROR ("Type was not set");
659         usage (argv[0]);
660         return -1;
661     }
662 
663     if (ins.empty () || outs.empty () ||
664             !strlen (ins[0]->get_file_name ()) || !strlen (outs[0]->get_file_name ())) {
665         XCAM_LOG_ERROR ("input or output file name was not set");
666         usage (argv[0]);
667         return -1;
668     }
669 
670     for (uint32_t i = 0; i < ins.size (); ++i) {
671         printf ("input%d file:\t\t%s\n", i, ins[i]->get_file_name ());
672     }
673     printf ("output file:\t\t%s\n", outs[0]->get_file_name ());
674     printf ("input width:\t\t%d\n", input_width);
675     printf ("input height:\t\t%d\n", input_height);
676     printf ("output width:\t\t%d\n", output_width);
677     printf ("output height:\t\t%d\n", output_height);
678     printf ("topview width:\t\t%d\n", topview_width);
679     printf ("topview height:\t\t%d\n", topview_height);
680     printf ("save output:\t\t%s\n", save_output ? "true" : "false");
681     printf ("loop count:\t\t%d\n", loop);
682 
683     VideoBufferInfo in_info, out_info;
684     in_info.init (V4L2_PIX_FMT_NV12, input_width, input_height);
685     out_info.init (V4L2_PIX_FMT_NV12, output_width, output_height);
686 
687     for (uint32_t i = 0; i < ins.size (); ++i) {
688         ins[i]->set_buf_size (input_width, input_height);
689         CHECK (ins[i]->create_buf_pool (in_info, 6), "create buffer pool failed");
690         CHECK (ins[i]->open_file ("rb"), "open file(%s) failed", ins[i]->get_file_name ());
691     }
692 
693     outs[0]->set_buf_size (output_width, output_height);
694     if (save_output) {
695         CHECK (ensure_output_format (outs[0]->get_file_name (), type, nv12_output), "unsupported output format");
696         if (nv12_output) {
697             CHECK (outs[0]->open_file ("wb"), "open file(%s) failed", outs[0]->get_file_name ());
698         }
699     }
700 
701     switch (type) {
702     case SoftTypeBlender: {
703         CHECK_EXP (ins.size () >= 2, "blender need 2 input files.");
704         SmartPtr<Blender> blender = Blender::create_soft_blender ();
705         XCAM_ASSERT (blender.ptr ());
706         blender->set_output_size (output_width, output_height);
707         Rect merge_window;
708         merge_window.pos_x = 0;
709         merge_window.pos_y = 0;
710         merge_window.width = out_info.width;
711         merge_window.height = out_info.height;
712         blender->set_merge_window (merge_window);
713 
714         CHECK (ins[0]->read_buf(), "read buffer from file(%s) failed.", ins[0]->get_file_name ());
715         CHECK (ins[1]->read_buf(), "read buffer from file(%s) failed.", ins[1]->get_file_name ());
716         RUN_N (blender->blend (ins[0]->get_buf (), ins[1]->get_buf (), outs[0]->get_buf ()), loop, "blend buffer failed.");
717         if (save_output)
718             outs[0]->write_buf ();
719         break;
720     }
721     case SoftTypeRemap: {
722         SmartPtr<GeoMapper> mapper = GeoMapper::create_soft_geo_mapper ();
723         XCAM_ASSERT (mapper.ptr ());
724         mapper->set_output_size (output_width, output_height);
725         mapper->set_lookup_table (map_table, MAP_WIDTH, MAP_HEIGHT);
726         //mapper->set_factors ((output_width - 1.0f) / (MAP_WIDTH - 1.0f), (output_height - 1.0f) / (MAP_HEIGHT - 1.0f));
727 
728         CHECK (ins[0]->read_buf(), "read buffer from file(%s) failed.", ins[0]->get_file_name ());
729         RUN_N (mapper->remap (ins[0]->get_buf (), outs[0]->get_buf ()), loop, "remap buffer failed.");
730         if (save_output)
731             outs[0]->write_buf ();
732         break;
733     }
734     case SoftTypeStitch: {
735         CHECK_EXP (ins.size () >= 2 && ins.size () <= 4, "stitcher need at 2~4 input files.");
736 
737         uint32_t camera_count = ins.size ();
738         SmartPtr<Stitcher> stitcher = Stitcher::create_soft_stitcher ();
739         XCAM_ASSERT (stitcher.ptr ());
740 
741         CameraInfo cam_info[4];
742         const char *fisheye_config_path = getenv ("FISHEYE_CONFIG_PATH");
743         if (!fisheye_config_path)
744             fisheye_config_path = FISHEYE_CONFIG_PATH;
745 
746         for (uint32_t i = 0; i < camera_count; ++i) {
747             if (parse_camera_info (fisheye_config_path, i, cam_info[i], camera_count) != 0) {
748                 XCAM_LOG_ERROR ("parse fisheye dewarp info(idx:%d) failed.", i);
749                 return -1;
750             }
751         }
752 
753         PointFloat3 bowl_coord_offset;
754         if (camera_count == 4) {
755             centralize_bowl_coord_from_cameras (
756                 cam_info[0].calibration.extrinsic, cam_info[1].calibration.extrinsic,
757                 cam_info[2].calibration.extrinsic, cam_info[3].calibration.extrinsic,
758                 bowl_coord_offset);
759         }
760 
761         stitcher->set_camera_num (camera_count);
762         for (uint32_t i = 0; i < camera_count; ++i) {
763             stitcher->set_camera_info (i, cam_info[i]);
764         }
765 
766         BowlDataConfig bowl;
767         bowl.wall_height = 3000.0f;
768         bowl.ground_length = 2000.0f;
769         //bowl.a = 5000.0f;
770         //bowl.b = 3600.0f;
771         //bowl.c = 3000.0f;
772         bowl.angle_start = 0.0f;
773         bowl.angle_end = 360.0f;
774         stitcher->set_bowl_config (bowl);
775         stitcher->set_output_size (output_width, output_height);
776 
777         if (save_output) {
778             add_element (outs, "topview", topview_width, topview_height);
779             elements_open_file (outs, "wb", nv12_output);
780         }
781         CHECK_EXP (
782             run_stitcher (stitcher, ins, outs, nv12_output, save_output, loop) == 0,
783             "run stitcher failed.");
784         break;
785     }
786 
787     default: {
788         XCAM_LOG_ERROR ("unsupported type:%d", type);
789         usage (argv[0]);
790         return -1;
791     }
792     }
793 
794     return 0;
795 }
796