cout << "Usage: opencv_stitching img1 img2 [...imgN]\n" \r
<< "\t[--trygpu (yes|no)]\n"\r
<< "\t[--work_megapix <float>]\n"\r
+ << "\t[--seam_megapix <float>]\n"\r
<< "\t[--compose_megapix <float>]\n"\r
- << "\t[--matchconf <float>]\n"\r
+ << "\t[--match_conf <float>]\n"\r
<< "\t[--ba (ray|focal_ray)]\n"\r
<< "\t[--conf_thresh <float>]\n"\r
<< "\t[--wavecorrect (no|yes)]\n"\r
<< "\t[--blend (no|feather|multiband)]\n"\r
<< "\t[--numbands <int>]\n"\r
<< "\t[--output <result_img>]\n\n";\r
- cout << "--matchconf\n"\r
+ cout << "--match_conf\n"\r
<< "\tGood values are in [0.2, 0.8] range usually.\n\n";\r
cout << "HINT:\n" \r
<< "\tDefault parameters are for '--trygpu no' configuration.\n"\r
}\r
\r
\r
-// Command line args\r
+// Default command line args\r
vector<string> img_names;\r
bool trygpu = false;\r
-double work_megapix = 0.2;\r
+double work_megapix = 0.3;\r
+double seam_megapix = 0.1;\r
double compose_megapix = 1;\r
int ba_space = BundleAdjuster::FOCAL_RAY_SPACE;\r
float conf_thresh = 1.f;\r
\r
for (int i = 1; i < argc; ++i)\r
{\r
- if (string(argv[i]) == "--work_megapix")\r
- {\r
- work_megapix = atof(argv[i + 1]);\r
- break;\r
- }\r
- }\r
-\r
- for (int i = 1; i < argc; ++i)\r
- {\r
if (string(argv[i]) == "--trygpu")\r
{\r
if (string(argv[i + 1]) == "no")\r
i++;\r
}\r
else if (string(argv[i]) == "--work_megapix") \r
+ {\r
+ work_megapix = atof(argv[i + 1]);\r
+ i++; \r
+ }\r
+ else if (string(argv[i]) == "--seam_megapix") \r
+ {\r
+ seam_megapix = atof(argv[i + 1]);\r
i++; \r
+ }\r
else if (string(argv[i]) == "--compose_megapix") \r
{\r
compose_megapix = atof(argv[i + 1]);\r
result_name = argv[i + 1];\r
i++;\r
}\r
- else if (string(argv[i]) == "--matchconf")\r
+ else if (string(argv[i]) == "--match_conf")\r
{\r
user_match_conf = true;\r
match_conf = static_cast<float>(atof(argv[i + 1]));\r
int num_images = static_cast<int>(img_names.size());\r
if (num_images < 2)\r
{\r
- cout << "Need more images\n";\r
+ LOGLN("Need more images");\r
return -1;\r
}\r
\r
- // We do all matching in work_scale sclae, and compositing in compose_scale scale\r
- double work_scale = 1, compose_scale = 1;\r
- bool is_work_scale_set = false, is_compose_scale_set = false;\r
+ double work_scale = 1, seam_scale = 1, compose_scale = 1;\r
+ bool is_work_scale_set = false, is_seam_scale_set = false, is_compose_scale_set = false;\r
\r
LOGLN("Reading images and finding features...");\r
int64 t = getTickCount();\r
- vector<Mat> images(num_images);\r
+\r
vector<ImageFeatures> features(num_images);\r
SurfFeaturesFinder finder(trygpu);\r
Mat full_img, img;\r
+\r
+ vector<Mat> images(num_images);\r
+ double seam_work_aspect = 1;\r
+\r
for (int i = 0; i < num_images; ++i)\r
{\r
full_img = imread(img_names[i]);\r
if (full_img.empty())\r
{\r
- cout << "Can't open image " << img_names[i] << endl;\r
+ LOGLN("Can't open image " << img_names[i]);\r
return -1;\r
}\r
if (work_megapix < 0)\r
+ {\r
img = full_img;\r
+ work_scale = 1;\r
+ is_work_scale_set = true;\r
+ }\r
else\r
{\r
if (!is_work_scale_set)\r
}\r
resize(full_img, img, Size(), work_scale, work_scale);\r
}\r
- images[i] = img.clone();\r
+ if (!is_seam_scale_set)\r
+ {\r
+ seam_scale = min(1.0, sqrt(seam_megapix * 1e6 / full_img.size().area())); \r
+ seam_work_aspect = seam_scale / work_scale;\r
+ is_seam_scale_set = true;\r
+ }\r
+\r
finder(img, features[i]);\r
+ LOGLN("Features in image #" << i << ": " << features[i].keypoints.size());\r
+\r
+ resize(full_img, img, Size(), seam_scale, seam_scale);\r
+ images[i] = img.clone();\r
}\r
+\r
full_img.release();\r
img.release();\r
+\r
LOGLN("Reading images and finding features, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");\r
\r
LOGLN("Pairwise matching... ");\r
vector<Mat> img_subset;\r
vector<string> img_names_subset;\r
for (size_t i = 0; i < indices.size(); ++i)\r
- {\r
- img_subset.push_back(images[indices[i]]);\r
img_names_subset.push_back(img_names[indices[i]]);\r
- }\r
- images = img_subset;\r
img_names = img_names_subset;\r
\r
// Check if we still have enough images\r
num_images = static_cast<int>(img_names.size());\r
if (num_images < 2)\r
{\r
- cout << "Need more images\n";\r
+ LOGLN("Need more images");\r
return -1;\r
}\r
\r
vector<Size> sizes(num_images);\r
vector<Mat> masks(num_images);\r
\r
- // Preapre original images masks\r
+ // Preapre images masks\r
for (int i = 0; i < num_images; ++i)\r
{\r
masks[i].create(images[i].size(), CV_8U);\r
}\r
\r
// Warp images and their masks\r
- Ptr<Warper> warper = Warper::createByCameraFocal(camera_focal, warp_type);\r
+ Ptr<Warper> warper = Warper::createByCameraFocal(static_cast<float>(camera_focal * seam_work_aspect), \r
+ warp_type);\r
for (int i = 0; i < num_images; ++i)\r
{\r
- corners[i] = warper->warp(images[i], static_cast<float>(cameras[i].focal), cameras[i].R, \r
- images_warped[i]);\r
+ corners[i] = warper->warp(images[i], static_cast<float>(cameras[i].focal * seam_work_aspect), \r
+ cameras[i].R, images_warped[i]);\r
sizes[i] = images_warped[i].size();\r
- warper->warp(masks[i], static_cast<float>(cameras[i].focal), cameras[i].R, masks_warped[i], \r
- INTER_NEAREST, BORDER_CONSTANT);\r
+ warper->warp(masks[i], static_cast<float>(cameras[i].focal * seam_work_aspect), \r
+ cameras[i].R, masks_warped[i], INTER_NEAREST, BORDER_CONSTANT);\r
}\r
\r
// Convert to float for blending\r
Mat img_warped, img_warped_f;\r
Mat dilated_mask, seam_mask, mask, mask_warped;\r
Ptr<Blender> blender;\r
- double compose_aspect = 1;\r
+ double compose_seam_aspect = 1;\r
+ double compose_work_aspect = 1;\r
\r
for (int img_idx = 0; img_idx < num_images; ++img_idx)\r
{\r
if (compose_megapix > 0)\r
compose_scale = min(1.0, sqrt(compose_megapix * 1e6 / full_img.size().area()));\r
is_compose_scale_set = true;\r
- compose_aspect = compose_scale / work_scale;\r
- camera_focal *= static_cast<float>(compose_aspect);\r
+ compose_seam_aspect = compose_scale / seam_scale;\r
+ compose_work_aspect = compose_scale / work_scale;\r
+ camera_focal *= static_cast<float>(compose_work_aspect);\r
warper = Warper::createByCameraFocal(camera_focal, warp_type);\r
}\r
if (abs(compose_scale - 1) > 1e-1)\r
full_img.release();\r
\r
// Update cameras paramters\r
- cameras[img_idx].focal *= compose_aspect;\r
+ cameras[img_idx].focal *= compose_work_aspect;\r
\r
// Warp the current image\r
warper->warp(img, static_cast<float>(cameras[img_idx].focal), cameras[img_idx].R, \r
Rect dst_roi = resultRoi(corners, sizes);\r
for (int i = 0; i < num_images; ++i)\r
{\r
- corners[i] = dst_roi.tl() + (corners[i] - dst_roi.tl()) * compose_aspect;\r
- sizes[i] = Size(static_cast<int>((sizes[i].width + 1) * compose_aspect), \r
- static_cast<int>((sizes[i].height + 1) * compose_aspect));\r
+ corners[i] = dst_roi.tl() + (corners[i] - dst_roi.tl()) * compose_seam_aspect;\r
+ sizes[i] = Size(static_cast<int>((sizes[i].width + 1) * compose_seam_aspect), \r
+ static_cast<int>((sizes[i].height + 1) * compose_seam_aspect));\r
}\r
blender->prepare(corners, sizes);\r
}\r