blob: 5280467b7c78fd44565694f67ed47c254f61bf1b [file] [log] [blame]
/*
* test-video-stabilization.cpp - test video stabilization using Gyroscope
*
* Copyright (c) 2017 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author: Zong Wei <wei.zong@intel.com>
*/
#include "test_common.h"
#include "test_inline.h"
#include <unistd.h>
#include <getopt.h>
#include <ocl/cl_utils.h>
#include <ocl/cl_device.h>
#include <ocl/cl_context.h>
#include <ocl/cl_blender.h>
#include <image_file_handle.h>
#include <ocl/cl_video_stabilizer.h>
#include <dma_video_buffer.h>
#if HAVE_OPENCV
#include <opencv2/opencv.hpp>
#include <opencv2/core/ocl.hpp>
#include <ocl/cv_base_class.h>
#endif
using namespace XCam;
static int read_device_pose (const char *file, DevicePoseList &pose, uint32_t pose_size);
static void
usage(const char* arg0)
{
printf ("Usage:\n"
"%s --input file --output file"
" [--input-w width] [--input-h height] \n"
"\t--input, input image(NV12)\n"
"\t--gyro, input gyro pose data;\n"
"\t--output, output image(NV12) PREFIX\n"
"\t--input-w, optional, input width; default:1920\n"
"\t--input-h, optional, input height; default:1080\n"
"\t--save, optional, save file or not, default true; select from [true/false]\n"
"\t--loop optional, how many loops need to run for performance test, default: 1\n"
"\t--help, usage\n",
arg0);
}
int main (int argc, char *argv[])
{
XCamReturn ret = XCAM_RETURN_NO_ERROR;
SmartPtr<CLVideoStabilizer> video_stab;
SmartPtr<CLContext> context;
SmartPtr<BufferPool> buf_pool;
VideoBufferInfo input_buf_info;
VideoBufferInfo output_buf_info;
SmartPtr<VideoBuffer> input_buf;
SmartPtr<VideoBuffer> output_buf;
uint32_t input_format = V4L2_PIX_FMT_NV12;
uint32_t input_width = 1920;
uint32_t input_height = 1080;
uint32_t output_width = 1920;
uint32_t output_height = 1080;
ImageFileHandle file_in, file_out;
const char *file_in_name = NULL;
const char *file_out_name = NULL;
const char *gyro_data = "gyro_data.csv";
bool need_save_output = true;
double framerate = 30.0;
int loop = 1;
const struct option long_opts[] = {
{"input", required_argument, NULL, 'i'},
{"gyro", required_argument, NULL, 'g'},
{"output", required_argument, NULL, 'o'},
{"input-w", required_argument, NULL, 'w'},
{"input-h", required_argument, NULL, 'h'},
{"save", required_argument, NULL, 's'},
{"loop", required_argument, NULL, 'l'},
{"help", no_argument, NULL, 'H'},
{0, 0, 0, 0},
};
int opt = -1;
while ((opt = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
switch (opt) {
case 'i':
file_in_name = optarg;
break;
case 'g':
gyro_data = optarg;
break;
case 'o':
file_out_name = optarg;
break;
case 'w':
input_width = atoi(optarg);
output_width = input_width;
break;
case 'h':
input_height = atoi(optarg);
output_height = input_height;
break;
case 's':
need_save_output = (strcasecmp (optarg, "false") == 0 ? false : true);
break;
case 'l':
loop = atoi(optarg);
break;
case 'H':
usage (argv[0]);
return -1;
default:
printf ("getopt_long return unknown value:%c\n", opt);
usage (argv[0]);
return -1;
}
}
if (optind < argc || argc < 2) {
printf("unknown option %s\n", argv[optind]);
usage (argv[0]);
return -1;
}
if (!file_in_name || !file_out_name) {
XCAM_LOG_ERROR ("input/output path is NULL");
return -1;
}
printf ("Description-----------\n");
printf ("input video file:%s\n", file_in_name);
printf ("gyro pose file:%s\n", gyro_data);
printf ("output file PREFIX:%s\n", file_out_name);
printf ("input width:%d\n", input_width);
printf ("input height:%d\n", input_height);
printf ("need save file:%s\n", need_save_output ? "true" : "false");
printf ("loop count:\t\t%d\n", loop);
printf ("----------------------\n");
DevicePoseList device_pose;
const int pose_size = sizeof(DevicePose::orientation) / sizeof(double) +
sizeof(DevicePose::translation) / sizeof(double) +
sizeof(DevicePose::timestamp) / sizeof(int64_t);
const int count = read_device_pose (gyro_data, device_pose, pose_size);
if (count <= 0 || device_pose.size () <= 0) {
XCAM_LOG_ERROR ("read gyro file(%s) failed.", gyro_data);
return -1;
}
context = CLDevice::instance ()->get_context ();
video_stab = create_cl_video_stab_handler (context).dynamic_cast_ptr<CLVideoStabilizer> ();
XCAM_ASSERT (video_stab.ptr ());
video_stab->set_pool_type (CLImageHandler::CLVideoPoolType);
/*
Color CameraIntrinsics:
image_width: 1920, image_height :1080,
fx: 1707.799171, fy: 1710.337510,
cx: 940.413257, cy: 540.198348,
image_plane_distance: 1.778957.
Color Camera Frame with respect to IMU Frame:
Position: 0.045699, -0.008592, -0.006434
Orientation: -0.013859, -0.999889, 0.002361, 0.005021
*/
double focal_x = 1707.799171;
double focal_y = 1710.337510;
double offset_x = 940.413257;
double offset_y = 540.198348;
double skew = 0;
video_stab->set_camera_intrinsics (focal_x, focal_y, offset_x, offset_y, skew);
CoordinateSystemConv world_to_device (AXIS_X, AXIS_MINUS_Z, AXIS_NONE);
CoordinateSystemConv device_to_image (AXIS_X, AXIS_Y, AXIS_Y);
video_stab->align_coordinate_system (world_to_device, device_to_image);
uint32_t radius = 15;
float stdev = 10;
video_stab->set_motion_filter (radius, stdev);
input_buf_info.init (input_format, input_width, input_height);
output_buf_info.init (input_format, output_width, output_height);
buf_pool = new CLVideoBufferPool ();
XCAM_ASSERT (buf_pool.ptr ());
buf_pool->set_video_info (input_buf_info);
if (!buf_pool->reserve (36)) {
XCAM_LOG_ERROR ("init buffer pool failed");
return -1;
}
ret = file_in.open (file_in_name, "rb");
CHECK (ret, "open %s failed", file_in_name);
#if HAVE_OPENCV
cv::VideoWriter writer;
if (need_save_output) {
cv::Size dst_size = cv::Size (output_width, output_height);
if (!writer.open (file_out_name, CV_FOURCC('X', '2', '6', '4'), framerate, dst_size)) {
XCAM_LOG_ERROR ("open file %s failed", file_out_name);
return -1;
}
}
#endif
int i = 0;
while (loop--) {
ret = file_in.rewind ();
CHECK (ret, "video stabilization stitch rewind file(%s) failed", file_in_name);
video_stab->reset_counter ();
DevicePoseList::iterator pose_iterator = device_pose.begin ();
do {
input_buf = buf_pool->get_buffer (buf_pool);
XCAM_ASSERT (input_buf.ptr ());
ret = file_in.read_buf (input_buf);
if (ret == XCAM_RETURN_BYPASS)
break;
if (ret == XCAM_RETURN_ERROR_FILE) {
XCAM_LOG_ERROR ("read buffer from %s failed", file_in_name);
return -1;
}
SmartPtr<MetaData> pose_data = *(pose_iterator);
SmartPtr<DevicePose> data = *(pose_iterator);
input_buf->add_metadata (pose_data);
input_buf->set_timestamp (pose_data->timestamp);
ret = video_stab->execute (input_buf, output_buf);
if (++pose_iterator == device_pose.end ()) {
break;
}
if (ret == XCAM_RETURN_BYPASS) {
continue;
}
#if HAVE_OPENCV
if (need_save_output) {
cv::Mat out_mat;
convert_to_mat (output_buf, out_mat);
writer.write (out_mat);
} else
#endif
ensure_gpu_buffer_done (output_buf);
FPS_CALCULATION (video_stabilizer, XCAM_OBJ_DUR_FRAME_NUM);
++i;
} while (true);
}
return ret;
}
//return count
#define RELEASE_FILE_MEM { \
xcam_free (ptr); \
if (p_f) fclose (p_f); \
return -1; \
}
int read_device_pose (const char* file, DevicePoseList &pose_list, uint32_t members)
{
char *ptr = NULL;
SmartPtr<DevicePose> data;
FILE *p_f = fopen (file, "rb");
CHECK_EXP (p_f, "open gyro pos data(%s) failed", file);
CHECK_DECLARE (
ERROR,
!fseek (p_f, 0L, SEEK_END),
RELEASE_FILE_MEM, "seek to file(%s) end failed", file);
size_t size = ftell(p_f);
int entries = size / members;
fseek (p_f, 0L, SEEK_SET);
ptr = (char*) xcam_malloc0 (size + 1);
CHECK_DECLARE (ERROR, ptr, RELEASE_FILE_MEM, "malloc file buffer failed");
CHECK_DECLARE (
ERROR,
fread (ptr, 1, size, p_f) == size,
RELEASE_FILE_MEM, "read pose file(%s)failed", file);
ptr[size] = 0;
fclose (p_f);
p_f = NULL;
char *str_num = NULL;
char tokens[] = "\t ,\r\n";
str_num = strtok (ptr, tokens);
int count = 0;
int x = 0, y = 0;
const int orient_size = sizeof(DevicePose::orientation) / sizeof(double);
const int trans_size = sizeof(DevicePose::translation) / sizeof(double);
while (str_num != NULL) {
float num = strtof (str_num, NULL);
x = count % members;
y = count / members;
if (y >= entries) {
break;
}
if (x == 0) {
data = new DevicePose ();
}
CHECK_DECLARE (ERROR, data.ptr (), RELEASE_FILE_MEM, "invalid buffer pointer(device pose is null)");
if (x < orient_size) {
data->orientation[x] = num;
} else if (x < orient_size + trans_size) {
data->translation[x - orient_size] = num;
} else if (x == orient_size + trans_size) {
data->timestamp = num * 1000000;
pose_list.push_back (data);
} else {
CHECK_DECLARE (ERROR, false, RELEASE_FILE_MEM, "unknow branch");
}
++count;
str_num = strtok (NULL, tokens);
}
free (ptr);
ptr = NULL;
return count / members;
}