added GC_COLOR_GRAD cost function type into opencv_stitching
This commit is contained in:
parent
8e3777676c
commit
56f7e54cce
@ -45,6 +45,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
using namespace cv::gpu;
|
||||||
|
|
||||||
|
|
||||||
Ptr<ExposureCompensator> ExposureCompensator::createDefault(int type)
|
Ptr<ExposureCompensator> ExposureCompensator::createDefault(int type)
|
||||||
@ -53,6 +54,8 @@ Ptr<ExposureCompensator> ExposureCompensator::createDefault(int type)
|
|||||||
return new NoExposureCompensator();
|
return new NoExposureCompensator();
|
||||||
if (type == OVERLAP)
|
if (type == OVERLAP)
|
||||||
return new OverlapExposureCompensator();
|
return new OverlapExposureCompensator();
|
||||||
|
if (type == SEGMENT)
|
||||||
|
return new SegmentExposureCompensator();
|
||||||
CV_Error(CV_StsBadArg, "unsupported exposure compensation method");
|
CV_Error(CV_StsBadArg, "unsupported exposure compensation method");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -61,13 +64,15 @@ Ptr<ExposureCompensator> ExposureCompensator::createDefault(int type)
|
|||||||
void OverlapExposureCompensator::feed(const vector<Point> &corners, const vector<Mat> &images,
|
void OverlapExposureCompensator::feed(const vector<Point> &corners, const vector<Mat> &images,
|
||||||
const vector<Mat> &masks)
|
const vector<Mat> &masks)
|
||||||
{
|
{
|
||||||
|
CV_Assert(corners.size() == images.size() && images.size() == masks.size());
|
||||||
|
|
||||||
const int num_images = static_cast<int>(images.size());
|
const int num_images = static_cast<int>(images.size());
|
||||||
Mat_<int> N(num_images, num_images); N.setTo(0);
|
Mat_<int> N(num_images, num_images); N.setTo(0);
|
||||||
Mat_<double> I(num_images, num_images); I.setTo(0);
|
Mat_<double> I(num_images, num_images); I.setTo(0);
|
||||||
|
|
||||||
Rect dst_roi = resultRoi(corners, images);
|
Rect dst_roi = resultRoi(corners, images);
|
||||||
Mat subimg1, subimg2;
|
Mat subimg1, subimg2;
|
||||||
Mat_<uchar> submask1, submask2, overlap;
|
Mat_<uchar> submask1, submask2, intersect;
|
||||||
|
|
||||||
for (int i = 0; i < num_images; ++i)
|
for (int i = 0; i < num_images; ++i)
|
||||||
{
|
{
|
||||||
@ -81,9 +86,9 @@ void OverlapExposureCompensator::feed(const vector<Point> &corners, const vector
|
|||||||
|
|
||||||
submask1 = masks[i](Rect(roi.tl() - corners[i], roi.br() - corners[i]));
|
submask1 = masks[i](Rect(roi.tl() - corners[i], roi.br() - corners[i]));
|
||||||
submask2 = masks[j](Rect(roi.tl() - corners[j], roi.br() - corners[j]));
|
submask2 = masks[j](Rect(roi.tl() - corners[j], roi.br() - corners[j]));
|
||||||
overlap = submask1 & submask2;
|
intersect = submask1 & submask2;
|
||||||
|
|
||||||
N(i, j) = N(j, i) = countNonZero(overlap);
|
N(i, j) = N(j, i) = countNonZero(intersect);
|
||||||
|
|
||||||
double Isum1 = 0, Isum2 = 0;
|
double Isum1 = 0, Isum2 = 0;
|
||||||
for (int y = 0; y < roi.height; ++y)
|
for (int y = 0; y < roi.height; ++y)
|
||||||
@ -92,7 +97,7 @@ void OverlapExposureCompensator::feed(const vector<Point> &corners, const vector
|
|||||||
const Point3_<uchar>* r2 = subimg2.ptr<Point3_<uchar> >(y);
|
const Point3_<uchar>* r2 = subimg2.ptr<Point3_<uchar> >(y);
|
||||||
for (int x = 0; x < roi.width; ++x)
|
for (int x = 0; x < roi.width; ++x)
|
||||||
{
|
{
|
||||||
if (overlap(y, x))
|
if (intersect(y, x))
|
||||||
{
|
{
|
||||||
Isum1 += sqrt(static_cast<double>(sqr(r1[x].x) + sqr(r1[x].y) + sqr(r1[x].z)));
|
Isum1 += sqrt(static_cast<double>(sqr(r1[x].x) + sqr(r1[x].y) + sqr(r1[x].z)));
|
||||||
Isum2 += sqrt(static_cast<double>(sqr(r2[x].x) + sqr(r2[x].y) + sqr(r2[x].z)));
|
Isum2 += sqrt(static_cast<double>(sqr(r2[x].x) + sqr(r2[x].y) + sqr(r2[x].z)));
|
||||||
@ -122,7 +127,7 @@ void OverlapExposureCompensator::feed(const vector<Point> &corners, const vector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
solve(A, b, gains_, DECOMP_SVD);
|
solve(A, b, gains_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -130,3 +135,14 @@ void OverlapExposureCompensator::apply(int index, Point /*corner*/, Mat &image,
|
|||||||
{
|
{
|
||||||
image *= gains_(index, 0);
|
image *= gains_(index, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SegmentExposureCompensator::feed(const vector<Point> &/*corners*/, const vector<Mat> &/*images*/,
|
||||||
|
const vector<Mat> &/*masks*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SegmentExposureCompensator::apply(int /*index*/, Point /*corner*/, Mat &/*image*/, const Mat &/*mask*/)
|
||||||
|
{
|
||||||
|
}
|
@ -48,7 +48,7 @@
|
|||||||
class ExposureCompensator
|
class ExposureCompensator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum { NO, OVERLAP };
|
enum { NO, OVERLAP, SEGMENT };
|
||||||
static cv::Ptr<ExposureCompensator> createDefault(int type);
|
static cv::Ptr<ExposureCompensator> createDefault(int type);
|
||||||
|
|
||||||
virtual void feed(const std::vector<cv::Point> &corners, const std::vector<cv::Mat> &images,
|
virtual void feed(const std::vector<cv::Point> &corners, const std::vector<cv::Mat> &images,
|
||||||
@ -78,4 +78,12 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class SegmentExposureCompensator : public ExposureCompensator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void feed(const std::vector<cv::Point> &corners, const std::vector<cv::Mat> &images,
|
||||||
|
const std::vector<cv::Mat> &masks);
|
||||||
|
void apply(int index, cv::Point corner, cv::Mat &image, const cv::Mat &mask);
|
||||||
|
};
|
||||||
|
|
||||||
#endif // __OPENCV_EXPOSURE_COMPENSATE_HPP__
|
#endif // __OPENCV_EXPOSURE_COMPENSATE_HPP__
|
@ -71,7 +71,7 @@ void printUsage()
|
|||||||
<< "\t[--wavecorrect (no|yes)]\n"
|
<< "\t[--wavecorrect (no|yes)]\n"
|
||||||
<< "\t[--warp (plane|cylindrical|spherical)]\n"
|
<< "\t[--warp (plane|cylindrical|spherical)]\n"
|
||||||
<< "\t[--exposcomp (no|overlap)]\n"
|
<< "\t[--exposcomp (no|overlap)]\n"
|
||||||
<< "\t[--seam (no|voronoi|graphcut)]\n"
|
<< "\t[--seam (no|voronoi|gc_color|gc_colorgrad)]\n"
|
||||||
<< "\t[--blend (no|feather|multiband)]\n"
|
<< "\t[--blend (no|feather|multiband)]\n"
|
||||||
<< "\t[--numbands <int>]\n"
|
<< "\t[--numbands <int>]\n"
|
||||||
<< "\t[--output <result_img>]\n\n";
|
<< "\t[--output <result_img>]\n\n";
|
||||||
@ -95,7 +95,7 @@ int warp_type = Warper::SPHERICAL;
|
|||||||
int expos_comp_type = ExposureCompensator::OVERLAP;
|
int expos_comp_type = ExposureCompensator::OVERLAP;
|
||||||
bool user_match_conf = false;
|
bool user_match_conf = false;
|
||||||
float match_conf = 0.6f;
|
float match_conf = 0.6f;
|
||||||
int seam_find_type = SeamFinder::GRAPH_CUT;
|
int seam_find_type = SeamFinder::GC_COLOR;
|
||||||
int blend_type = Blender::MULTI_BAND;
|
int blend_type = Blender::MULTI_BAND;
|
||||||
int numbands = 5;
|
int numbands = 5;
|
||||||
string result_name = "result.png";
|
string result_name = "result.png";
|
||||||
@ -201,6 +201,8 @@ int parseCmdArgs(int argc, char** argv)
|
|||||||
expos_comp_type = ExposureCompensator::NO;
|
expos_comp_type = ExposureCompensator::NO;
|
||||||
else if (string(argv[i + 1]) == "overlap")
|
else if (string(argv[i + 1]) == "overlap")
|
||||||
expos_comp_type = ExposureCompensator::OVERLAP;
|
expos_comp_type = ExposureCompensator::OVERLAP;
|
||||||
|
else if (string(argv[i + 1]) == "segment")
|
||||||
|
expos_comp_type = ExposureCompensator::SEGMENT;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cout << "Bad exposure compensation method\n";
|
cout << "Bad exposure compensation method\n";
|
||||||
@ -214,8 +216,10 @@ int parseCmdArgs(int argc, char** argv)
|
|||||||
seam_find_type = SeamFinder::NO;
|
seam_find_type = SeamFinder::NO;
|
||||||
else if (string(argv[i + 1]) == "voronoi")
|
else if (string(argv[i + 1]) == "voronoi")
|
||||||
seam_find_type = SeamFinder::VORONOI;
|
seam_find_type = SeamFinder::VORONOI;
|
||||||
else if (string(argv[i + 1]) == "graphcut")
|
else if (string(argv[i + 1]) == "gc_color")
|
||||||
seam_find_type = SeamFinder::GRAPH_CUT;
|
seam_find_type = SeamFinder::GC_COLOR;
|
||||||
|
else if (string(argv[i + 1]) == "gc_colorgrad")
|
||||||
|
seam_find_type = SeamFinder::GC_COLOR_GRAD;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cout << "Bad seam finding method\n";
|
cout << "Bad seam finding method\n";
|
||||||
|
@ -65,8 +65,8 @@ namespace
|
|||||||
class CpuSurfFeaturesFinder : public FeaturesFinder
|
class CpuSurfFeaturesFinder : public FeaturesFinder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
inline CpuSurfFeaturesFinder(double hess_thresh, int num_octaves, int num_layers,
|
CpuSurfFeaturesFinder(double hess_thresh, int num_octaves, int num_layers,
|
||||||
int num_octaves_descr, int num_layers_descr)
|
int num_octaves_descr, int num_layers_descr)
|
||||||
{
|
{
|
||||||
detector_ = new SurfFeatureDetector(hess_thresh, num_octaves, num_layers);
|
detector_ = new SurfFeatureDetector(hess_thresh, num_octaves, num_layers);
|
||||||
extractor_ = new SurfDescriptorExtractor(num_octaves_descr, num_layers_descr);
|
extractor_ = new SurfDescriptorExtractor(num_octaves_descr, num_layers_descr);
|
||||||
@ -80,20 +80,12 @@ namespace
|
|||||||
Ptr<DescriptorExtractor> extractor_;
|
Ptr<DescriptorExtractor> extractor_;
|
||||||
};
|
};
|
||||||
|
|
||||||
void CpuSurfFeaturesFinder::find(const Mat &image, ImageFeatures &features)
|
|
||||||
{
|
|
||||||
Mat gray_image;
|
|
||||||
CV_Assert(image.depth() == CV_8U);
|
|
||||||
cvtColor(image, gray_image, CV_BGR2GRAY);
|
|
||||||
detector_->detect(gray_image, features.keypoints);
|
|
||||||
extractor_->compute(gray_image, features.keypoints, features.descriptors);
|
|
||||||
}
|
|
||||||
|
|
||||||
class GpuSurfFeaturesFinder : public FeaturesFinder
|
class GpuSurfFeaturesFinder : public FeaturesFinder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
inline GpuSurfFeaturesFinder(double hess_thresh, int num_octaves, int num_layers,
|
GpuSurfFeaturesFinder(double hess_thresh, int num_octaves, int num_layers,
|
||||||
int num_octaves_descr, int num_layers_descr)
|
int num_octaves_descr, int num_layers_descr)
|
||||||
{
|
{
|
||||||
surf_.keypointsRatio = 0.1f;
|
surf_.keypointsRatio = 0.1f;
|
||||||
surf_.hessianThreshold = hess_thresh;
|
surf_.hessianThreshold = hess_thresh;
|
||||||
@ -113,6 +105,17 @@ namespace
|
|||||||
int num_octaves_descr_, num_layers_descr_;
|
int num_octaves_descr_, num_layers_descr_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void CpuSurfFeaturesFinder::find(const Mat &image, ImageFeatures &features)
|
||||||
|
{
|
||||||
|
Mat gray_image;
|
||||||
|
CV_Assert(image.depth() == CV_8U);
|
||||||
|
cvtColor(image, gray_image, CV_BGR2GRAY);
|
||||||
|
detector_->detect(gray_image, features.keypoints);
|
||||||
|
extractor_->compute(gray_image, features.keypoints, features.descriptors);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void GpuSurfFeaturesFinder::find(const Mat &image, ImageFeatures &features)
|
void GpuSurfFeaturesFinder::find(const Mat &image, ImageFeatures &features)
|
||||||
{
|
{
|
||||||
GpuMat gray_image;
|
GpuMat gray_image;
|
||||||
@ -132,7 +135,8 @@ namespace
|
|||||||
|
|
||||||
d_descriptors.download(features.descriptors);
|
d_descriptors.download(features.descriptors);
|
||||||
}
|
}
|
||||||
}
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
SurfFeaturesFinder::SurfFeaturesFinder(bool try_use_gpu, double hess_thresh, int num_octaves, int num_layers,
|
SurfFeaturesFinder::SurfFeaturesFinder(bool try_use_gpu, double hess_thresh, int num_octaves, int num_layers,
|
||||||
int num_octaves_descr, int num_layers_descr)
|
int num_octaves_descr, int num_layers_descr)
|
||||||
|
@ -52,8 +52,10 @@ Ptr<SeamFinder> SeamFinder::createDefault(int type)
|
|||||||
return new NoSeamFinder();
|
return new NoSeamFinder();
|
||||||
if (type == VORONOI)
|
if (type == VORONOI)
|
||||||
return new VoronoiSeamFinder();
|
return new VoronoiSeamFinder();
|
||||||
if (type == GRAPH_CUT)
|
if (type == GC_COLOR)
|
||||||
return new GraphCutSeamFinder();
|
return new GraphCutSeamFinder(GraphCutSeamFinder::COST_COLOR);
|
||||||
|
if (type == GC_COLOR_GRAD)
|
||||||
|
return new GraphCutSeamFinder(GraphCutSeamFinder::COST_COLOR_GRAD);
|
||||||
CV_Error(CV_StsBadArg, "unsupported seam finding method");
|
CV_Error(CV_StsBadArg, "unsupported seam finding method");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -64,25 +66,33 @@ void PairwiseSeamFinder::find(const vector<Mat> &src, const vector<Point> &corne
|
|||||||
{
|
{
|
||||||
if (src.size() == 0)
|
if (src.size() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
images_ = src;
|
||||||
|
corners_ = corners;
|
||||||
|
masks_ = masks;
|
||||||
|
|
||||||
for (size_t i = 0; i < src.size() - 1; ++i)
|
for (size_t i = 0; i < src.size() - 1; ++i)
|
||||||
{
|
{
|
||||||
for (size_t j = i + 1; j < src.size(); ++j)
|
for (size_t j = i + 1; j < src.size(); ++j)
|
||||||
{
|
{
|
||||||
Rect roi;
|
Rect roi;
|
||||||
if (overlapRoi(corners[i], corners[j], src[i].size(), src[j].size(), roi))
|
if (overlapRoi(corners[i], corners[j], src[i].size(), src[j].size(), roi))
|
||||||
findInPair(src[i], src[j], corners[i], corners[j], roi, masks[i], masks[j]);
|
findInPair(i, j, roi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void VoronoiSeamFinder::findInPair(const Mat &img1, const Mat &img2, Point tl1, Point tl2,
|
void VoronoiSeamFinder::findInPair(size_t first, size_t second, Rect roi)
|
||||||
Rect roi, Mat &mask1, Mat &mask2)
|
|
||||||
{
|
{
|
||||||
const int gap = 10;
|
const int gap = 10;
|
||||||
Mat submask1(roi.height + 2 * gap, roi.width + 2 * gap, CV_8U);
|
Mat submask1(roi.height + 2 * gap, roi.width + 2 * gap, CV_8U);
|
||||||
Mat submask2(roi.height + 2 * gap, roi.width + 2 * gap, CV_8U);
|
Mat submask2(roi.height + 2 * gap, roi.width + 2 * gap, CV_8U);
|
||||||
|
|
||||||
|
Mat img1 = images_[first], img2 = images_[second];
|
||||||
|
Mat mask1 = masks_[first], mask2 = masks_[second];
|
||||||
|
Point tl1 = corners_[first], tl2 = corners_[second];
|
||||||
|
|
||||||
// Cut submasks with some gap
|
// Cut submasks with some gap
|
||||||
for (int y = -gap; y < roi.height + gap; ++y)
|
for (int y = -gap; y < roi.height + gap; ++y)
|
||||||
{
|
{
|
||||||
@ -127,27 +137,62 @@ void VoronoiSeamFinder::findInPair(const Mat &img1, const Mat &img2, Point tl1,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class GraphCutSeamFinder::Impl
|
class GraphCutSeamFinder::Impl : public PairwiseSeamFinder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Impl(int cost_type, float terminal_cost, float bad_region_penalty)
|
Impl(int cost_type, float terminal_cost, float bad_region_penalty)
|
||||||
: cost_type_(cost_type), terminal_cost_(terminal_cost), bad_region_penalty_(bad_region_penalty) {}
|
: cost_type_(cost_type), terminal_cost_(terminal_cost), bad_region_penalty_(bad_region_penalty) {}
|
||||||
|
|
||||||
void findInPair(const Mat &img1, const Mat &img2, Point tl1, Point tl2,
|
void find(const vector<Mat> &src, const vector<Point> &corners, vector<Mat> &masks);
|
||||||
Rect roi, Mat &mask1, Mat &mask2);
|
void findInPair(size_t first, size_t second, Rect roi);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setGraphWeightsColor(const Mat &img1, const Mat &img2, const Mat &mask1, const Mat &mask2,
|
void setGraphWeightsColor(const Mat &img1, const Mat &img2,
|
||||||
GCGraph<float> &graph);
|
const Mat &mask1, const Mat &mask2, GCGraph<float> &graph);
|
||||||
|
void setGraphWeightsColorGrad(const Mat &img1, const Mat &img2, const Mat &dx1, const Mat &dx2,
|
||||||
|
const Mat &dy1, const Mat &dy2, const Mat &mask1, const Mat &mask2,
|
||||||
|
GCGraph<float> &graph);
|
||||||
|
|
||||||
|
vector<Mat> dx_, dy_;
|
||||||
int cost_type_;
|
int cost_type_;
|
||||||
float terminal_cost_;
|
float terminal_cost_;
|
||||||
float bad_region_penalty_;
|
float bad_region_penalty_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void GraphCutSeamFinder::Impl::setGraphWeightsColor(const Mat &img1, const Mat &img2, const Mat &mask1, const Mat &mask2,
|
void GraphCutSeamFinder::Impl::find(const vector<Mat> &src, const vector<Point> &corners,
|
||||||
GCGraph<float> &graph)
|
vector<Mat> &masks)
|
||||||
|
{
|
||||||
|
// Compute gradients
|
||||||
|
dx_.resize(src.size());
|
||||||
|
dy_.resize(src.size());
|
||||||
|
Mat dx, dy;
|
||||||
|
for (size_t i = 0; i < src.size(); ++i)
|
||||||
|
{
|
||||||
|
CV_Assert(src[i].channels() == 3);
|
||||||
|
Sobel(src[i], dx, CV_32F, 1, 0);
|
||||||
|
Sobel(src[i], dy, CV_32F, 0, 1);
|
||||||
|
dx_[i].create(src[i].size(), CV_32F);
|
||||||
|
dy_[i].create(src[i].size(), CV_32F);
|
||||||
|
for (int y = 0; y < src[i].rows; ++y)
|
||||||
|
{
|
||||||
|
const Point3f* dx_row = dx.ptr<Point3f>(y);
|
||||||
|
const Point3f* dy_row = dy.ptr<Point3f>(y);
|
||||||
|
float* dx_row_ = dx_[i].ptr<float>(y);
|
||||||
|
float* dy_row_ = dy_[i].ptr<float>(y);
|
||||||
|
for (int x = 0; x < src[i].cols; ++x)
|
||||||
|
{
|
||||||
|
dx_row_[x] = normL2(dx_row[x]);
|
||||||
|
dy_row_[x] = normL2(dy_row[x]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PairwiseSeamFinder::find(src, corners, masks);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GraphCutSeamFinder::Impl::setGraphWeightsColor(const Mat &img1, const Mat &img2,
|
||||||
|
const Mat &mask1, const Mat &mask2, GCGraph<float> &graph)
|
||||||
{
|
{
|
||||||
const Size img_size = img1.size();
|
const Size img_size = img1.size();
|
||||||
|
|
||||||
@ -162,9 +207,8 @@ void GraphCutSeamFinder::Impl::setGraphWeightsColor(const Mat &img1, const Mat &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const float weight_eps = 1e-3f;
|
|
||||||
|
|
||||||
// Set regular edge weights
|
// Set regular edge weights
|
||||||
|
const float weight_eps = 1e-3f;
|
||||||
for (int y = 0; y < img_size.height; ++y)
|
for (int y = 0; y < img_size.height; ++y)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < img_size.width; ++x)
|
for (int x = 0; x < img_size.width; ++x)
|
||||||
@ -175,11 +219,9 @@ void GraphCutSeamFinder::Impl::setGraphWeightsColor(const Mat &img1, const Mat &
|
|||||||
float weight = normL2(img1.at<Point3f>(y, x), img2.at<Point3f>(y, x)) +
|
float weight = normL2(img1.at<Point3f>(y, x), img2.at<Point3f>(y, x)) +
|
||||||
normL2(img1.at<Point3f>(y, x + 1), img2.at<Point3f>(y, x + 1)) +
|
normL2(img1.at<Point3f>(y, x + 1), img2.at<Point3f>(y, x + 1)) +
|
||||||
weight_eps;
|
weight_eps;
|
||||||
|
|
||||||
if (!mask1.at<uchar>(y, x) || !mask1.at<uchar>(y, x + 1) ||
|
if (!mask1.at<uchar>(y, x) || !mask1.at<uchar>(y, x + 1) ||
|
||||||
!mask2.at<uchar>(y, x) || !mask2.at<uchar>(y, x + 1))
|
!mask2.at<uchar>(y, x) || !mask2.at<uchar>(y, x + 1))
|
||||||
weight += bad_region_penalty_;
|
weight += bad_region_penalty_;
|
||||||
|
|
||||||
graph.addEdges(v, v + 1, weight, weight);
|
graph.addEdges(v, v + 1, weight, weight);
|
||||||
}
|
}
|
||||||
if (y < img_size.height - 1)
|
if (y < img_size.height - 1)
|
||||||
@ -187,11 +229,9 @@ void GraphCutSeamFinder::Impl::setGraphWeightsColor(const Mat &img1, const Mat &
|
|||||||
float weight = normL2(img1.at<Point3f>(y, x), img2.at<Point3f>(y, x)) +
|
float weight = normL2(img1.at<Point3f>(y, x), img2.at<Point3f>(y, x)) +
|
||||||
normL2(img1.at<Point3f>(y + 1, x), img2.at<Point3f>(y + 1, x)) +
|
normL2(img1.at<Point3f>(y + 1, x), img2.at<Point3f>(y + 1, x)) +
|
||||||
weight_eps;
|
weight_eps;
|
||||||
|
|
||||||
if (!mask1.at<uchar>(y, x) || !mask1.at<uchar>(y + 1, x) ||
|
if (!mask1.at<uchar>(y, x) || !mask1.at<uchar>(y + 1, x) ||
|
||||||
!mask2.at<uchar>(y, x) || !mask2.at<uchar>(y + 1, x))
|
!mask2.at<uchar>(y, x) || !mask2.at<uchar>(y + 1, x))
|
||||||
weight += bad_region_penalty_;
|
weight += bad_region_penalty_;
|
||||||
|
|
||||||
graph.addEdges(v, v + img_size.width, weight, weight);
|
graph.addEdges(v, v + img_size.width, weight, weight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,15 +239,77 @@ void GraphCutSeamFinder::Impl::setGraphWeightsColor(const Mat &img1, const Mat &
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void GraphCutSeamFinder::Impl::findInPair(const Mat &img1, const Mat &img2, Point tl1, Point tl2,
|
void GraphCutSeamFinder::Impl::setGraphWeightsColorGrad(
|
||||||
Rect roi, Mat &mask1, Mat &mask2)
|
const Mat &img1, const Mat &img2, const Mat &dx1, const Mat &dx2,
|
||||||
|
const Mat &dy1, const Mat &dy2, const Mat &mask1, const Mat &mask2,
|
||||||
|
GCGraph<float> &graph)
|
||||||
{
|
{
|
||||||
const int gap = 10;
|
const Size img_size = img1.size();
|
||||||
|
|
||||||
|
// Set terminal weights
|
||||||
|
for (int y = 0; y < img_size.height; ++y)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < img_size.width; ++x)
|
||||||
|
{
|
||||||
|
int v = graph.addVtx();
|
||||||
|
graph.addTermWeights(v, mask1.at<uchar>(y, x) ? terminal_cost_ : 0.f,
|
||||||
|
mask2.at<uchar>(y, x) ? terminal_cost_ : 0.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set regular edge weights
|
||||||
|
const float weight_eps = 1e-3f;
|
||||||
|
for (int y = 0; y < img_size.height; ++y)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < img_size.width; ++x)
|
||||||
|
{
|
||||||
|
int v = y * img_size.width + x;
|
||||||
|
if (x < img_size.width - 1)
|
||||||
|
{
|
||||||
|
float grad = dx1.at<float>(y, x) + dx1.at<float>(y, x + 1) +
|
||||||
|
dx2.at<float>(y, x) + dx2.at<float>(y, x + 1) + weight_eps;
|
||||||
|
float weight = (normL2(img1.at<Point3f>(y, x), img2.at<Point3f>(y, x)) +
|
||||||
|
normL2(img1.at<Point3f>(y, x + 1), img2.at<Point3f>(y, x + 1))) / grad +
|
||||||
|
weight_eps;
|
||||||
|
if (!mask1.at<uchar>(y, x) || !mask1.at<uchar>(y, x + 1) ||
|
||||||
|
!mask2.at<uchar>(y, x) || !mask2.at<uchar>(y, x + 1))
|
||||||
|
weight += bad_region_penalty_;
|
||||||
|
graph.addEdges(v, v + 1, weight, weight);
|
||||||
|
}
|
||||||
|
if (y < img_size.height - 1)
|
||||||
|
{
|
||||||
|
float grad = dy1.at<float>(y, x) + dy1.at<float>(y + 1, x) +
|
||||||
|
dy2.at<float>(y, x) + dy2.at<float>(y + 1, x) + weight_eps;
|
||||||
|
float weight = (normL2(img1.at<Point3f>(y, x), img2.at<Point3f>(y, x)) +
|
||||||
|
normL2(img1.at<Point3f>(y + 1, x), img2.at<Point3f>(y + 1, x))) / grad +
|
||||||
|
weight_eps;
|
||||||
|
if (!mask1.at<uchar>(y, x) || !mask1.at<uchar>(y + 1, x) ||
|
||||||
|
!mask2.at<uchar>(y, x) || !mask2.at<uchar>(y + 1, x))
|
||||||
|
weight += bad_region_penalty_;
|
||||||
|
graph.addEdges(v, v + img_size.width, weight, weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GraphCutSeamFinder::Impl::findInPair(size_t first, size_t second, Rect roi)
|
||||||
|
{
|
||||||
|
Mat img1 = images_[first], img2 = images_[second];
|
||||||
|
Mat dx1 = dx_[first], dx2 = dx_[second];
|
||||||
|
Mat dy1 = dy_[first], dy2 = dy_[second];
|
||||||
|
Mat mask1 = masks_[first], mask2 = masks_[second];
|
||||||
|
Point tl1 = corners_[first], tl2 = corners_[second];
|
||||||
|
|
||||||
|
const int gap = 10;
|
||||||
Mat subimg1(roi.height + 2 * gap, roi.width + 2 * gap, CV_32FC3);
|
Mat subimg1(roi.height + 2 * gap, roi.width + 2 * gap, CV_32FC3);
|
||||||
Mat subimg2(roi.height + 2 * gap, roi.width + 2 * gap, CV_32FC3);
|
Mat subimg2(roi.height + 2 * gap, roi.width + 2 * gap, CV_32FC3);
|
||||||
Mat submask1(roi.height + 2 * gap, roi.width + 2 * gap, CV_8U);
|
Mat submask1(roi.height + 2 * gap, roi.width + 2 * gap, CV_8U);
|
||||||
Mat submask2(roi.height + 2 * gap, roi.width + 2 * gap, CV_8U);
|
Mat submask2(roi.height + 2 * gap, roi.width + 2 * gap, CV_8U);
|
||||||
|
Mat subdx1(roi.height + 2 * gap, roi.width + 2 * gap, CV_32F);
|
||||||
|
Mat subdy1(roi.height + 2 * gap, roi.width + 2 * gap, CV_32F);
|
||||||
|
Mat subdx2(roi.height + 2 * gap, roi.width + 2 * gap, CV_32F);
|
||||||
|
Mat subdy2(roi.height + 2 * gap, roi.width + 2 * gap, CV_32F);
|
||||||
|
|
||||||
// Cut subimages and submasks with some gap
|
// Cut subimages and submasks with some gap
|
||||||
for (int y = -gap; y < roi.height + gap; ++y)
|
for (int y = -gap; y < roi.height + gap; ++y)
|
||||||
@ -220,11 +322,15 @@ void GraphCutSeamFinder::Impl::findInPair(const Mat &img1, const Mat &img2, Poin
|
|||||||
{
|
{
|
||||||
subimg1.at<Point3f>(y + gap, x + gap) = img1.at<Point3f>(y1, x1);
|
subimg1.at<Point3f>(y + gap, x + gap) = img1.at<Point3f>(y1, x1);
|
||||||
submask1.at<uchar>(y + gap, x + gap) = mask1.at<uchar>(y1, x1);
|
submask1.at<uchar>(y + gap, x + gap) = mask1.at<uchar>(y1, x1);
|
||||||
|
subdx1.at<float>(y + gap, x + gap) = dx1.at<float>(y1, x1);
|
||||||
|
subdy1.at<float>(y + gap, x + gap) = dy1.at<float>(y1, x1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
subimg1.at<Point3f>(y + gap, x + gap) = Point3f(0, 0, 0);
|
subimg1.at<Point3f>(y + gap, x + gap) = Point3f(0, 0, 0);
|
||||||
submask1.at<uchar>(y + gap, x + gap) = 0;
|
submask1.at<uchar>(y + gap, x + gap) = 0;
|
||||||
|
subdx1.at<float>(y + gap, x + gap) = 0.f;
|
||||||
|
subdy1.at<float>(y + gap, x + gap) = 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
int y2 = roi.y - tl2.y + y;
|
int y2 = roi.y - tl2.y + y;
|
||||||
@ -233,11 +339,15 @@ void GraphCutSeamFinder::Impl::findInPair(const Mat &img1, const Mat &img2, Poin
|
|||||||
{
|
{
|
||||||
subimg2.at<Point3f>(y + gap, x + gap) = img2.at<Point3f>(y2, x2);
|
subimg2.at<Point3f>(y + gap, x + gap) = img2.at<Point3f>(y2, x2);
|
||||||
submask2.at<uchar>(y + gap, x + gap) = mask2.at<uchar>(y2, x2);
|
submask2.at<uchar>(y + gap, x + gap) = mask2.at<uchar>(y2, x2);
|
||||||
|
subdx2.at<float>(y + gap, x + gap) = dx2.at<float>(y2, x2);
|
||||||
|
subdy2.at<float>(y + gap, x + gap) = dy2.at<float>(y2, x2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
subimg2.at<Point3f>(y + gap, x + gap) = Point3f(0, 0, 0);
|
subimg2.at<Point3f>(y + gap, x + gap) = Point3f(0, 0, 0);
|
||||||
submask2.at<uchar>(y + gap, x + gap) = 0;
|
submask2.at<uchar>(y + gap, x + gap) = 0;
|
||||||
|
subdx2.at<float>(y + gap, x + gap) = 0.f;
|
||||||
|
subdy2.at<float>(y + gap, x + gap) = 0.f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -252,6 +362,10 @@ void GraphCutSeamFinder::Impl::findInPair(const Mat &img1, const Mat &img2, Poin
|
|||||||
case GraphCutSeamFinder::COST_COLOR:
|
case GraphCutSeamFinder::COST_COLOR:
|
||||||
setGraphWeightsColor(subimg1, subimg2, submask1, submask2, graph);
|
setGraphWeightsColor(subimg1, subimg2, submask1, submask2, graph);
|
||||||
break;
|
break;
|
||||||
|
case GraphCutSeamFinder::COST_COLOR_GRAD:
|
||||||
|
setGraphWeightsColorGrad(subimg1, subimg2, subdx1, subdx2, subdy1, subdy2,
|
||||||
|
submask1, submask2, graph);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
CV_Error(CV_StsBadArg, "unsupported pixel similarity measure");
|
CV_Error(CV_StsBadArg, "unsupported pixel similarity measure");
|
||||||
}
|
}
|
||||||
@ -281,8 +395,8 @@ GraphCutSeamFinder::GraphCutSeamFinder(int cost_type, float terminal_cost, float
|
|||||||
: impl_(new Impl(cost_type, terminal_cost, bad_region_penalty)) {}
|
: impl_(new Impl(cost_type, terminal_cost, bad_region_penalty)) {}
|
||||||
|
|
||||||
|
|
||||||
void GraphCutSeamFinder::findInPair(const Mat &img1, const Mat &img2, Point tl1, Point tl2,
|
void GraphCutSeamFinder::find(const vector<Mat> &src, const vector<Point> &corners,
|
||||||
Rect roi, Mat &mask1, Mat &mask2)
|
vector<Mat> &masks)
|
||||||
{
|
{
|
||||||
impl_->findInPair(img1, img2, tl1, tl2, roi, mask1, mask2);
|
impl_->find(src, corners, masks);
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
class SeamFinder
|
class SeamFinder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum { NO, VORONOI, GRAPH_CUT };
|
enum { NO, VORONOI, GC_COLOR, GC_COLOR_GRAD };
|
||||||
static cv::Ptr<SeamFinder> createDefault(int type);
|
static cv::Ptr<SeamFinder> createDefault(int type);
|
||||||
|
|
||||||
virtual ~SeamFinder() {}
|
virtual ~SeamFinder() {}
|
||||||
@ -66,34 +66,36 @@ public:
|
|||||||
class PairwiseSeamFinder : public SeamFinder
|
class PairwiseSeamFinder : public SeamFinder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void find(const std::vector<cv::Mat> &src, const std::vector<cv::Point> &corners,
|
virtual void find(const std::vector<cv::Mat> &src, const std::vector<cv::Point> &corners,
|
||||||
std::vector<cv::Mat> &masks);
|
std::vector<cv::Mat> &masks);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void findInPair(const cv::Mat &img1, const cv::Mat &img2, cv::Point tl1, cv::Point tl2,
|
virtual void findInPair(size_t first, size_t second, cv::Rect roi) = 0;
|
||||||
cv::Rect roi, cv::Mat &mask1, cv::Mat &mask2) = 0;
|
|
||||||
|
std::vector<cv::Mat> images_;
|
||||||
|
std::vector<cv::Point> corners_;
|
||||||
|
std::vector<cv::Mat> masks_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class VoronoiSeamFinder : public PairwiseSeamFinder
|
class VoronoiSeamFinder : public PairwiseSeamFinder
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
void findInPair(const cv::Mat &img1, const cv::Mat &img2, cv::Point tl1, cv::Point tl2,
|
void findInPair(size_t first, size_t second, cv::Rect roi);
|
||||||
cv::Rect roi, cv::Mat &mask1, cv::Mat &mask2);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class GraphCutSeamFinder : public PairwiseSeamFinder
|
class GraphCutSeamFinder : public SeamFinder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// TODO add COST_COLOR_GRAD support
|
enum { COST_COLOR, COST_COLOR_GRAD };
|
||||||
enum { COST_COLOR };
|
GraphCutSeamFinder(int cost_type = COST_COLOR_GRAD, float terminal_cost = 10000.f,
|
||||||
GraphCutSeamFinder(int cost_type = COST_COLOR, float terminal_cost = 10000.f,
|
|
||||||
float bad_region_penalty = 1000.f);
|
float bad_region_penalty = 1000.f);
|
||||||
|
|
||||||
private:
|
void find(const std::vector<cv::Mat> &src, const std::vector<cv::Point> &corners,
|
||||||
void findInPair(const cv::Mat &img1, const cv::Mat &img2, cv::Point tl1, cv::Point tl2,
|
std::vector<cv::Mat> &masks);
|
||||||
cv::Rect roi, cv::Mat &mask1, cv::Mat &mask2);
|
|
||||||
|
|
||||||
|
private:
|
||||||
class Impl;
|
class Impl;
|
||||||
cv::Ptr<Impl> impl_;
|
cv::Ptr<Impl> impl_;
|
||||||
};
|
};
|
||||||
|
@ -91,10 +91,17 @@ B Graph::walkBreadthFirst(int from, B body) const
|
|||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
// Some auxiliary math functions
|
// Some auxiliary math functions
|
||||||
|
|
||||||
|
static inline
|
||||||
|
float normL2(const cv::Point3f& a)
|
||||||
|
{
|
||||||
|
return a.x * a.x + a.y * a.y + a.z * a.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline
|
static inline
|
||||||
float normL2(const cv::Point3f& a, const cv::Point3f& b)
|
float normL2(const cv::Point3f& a, const cv::Point3f& b)
|
||||||
{
|
{
|
||||||
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z);
|
return normL2(a - b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user