diff --git a/modules/ximgproc/CMakeLists.txt b/modules/ximgproc/CMakeLists.txt index f6f88bec66d..5875db25cd0 100644 --- a/modules/ximgproc/CMakeLists.txt +++ b/modules/ximgproc/CMakeLists.txt @@ -1,2 +1,2 @@ set(the_description "Extended image processing module. It includes edge-aware filters and etc.") -ocv_define_module(ximgproc opencv_core opencv_imgproc opencv_calib3d opencv_imgcodecs WRAP python java) +ocv_define_module(ximgproc opencv_core opencv_highgui opencv_imgproc opencv_calib3d opencv_imgcodecs WRAP python java) diff --git a/modules/ximgproc/README.md b/modules/ximgproc/README.md index 59e744e0238..be37294a798 100644 --- a/modules/ximgproc/README.md +++ b/modules/ximgproc/README.md @@ -16,3 +16,4 @@ Extended Image Processing - Pei&Lin Normalization - Ridge Detection Filter - Binary morphology on run-length encoded images +- Global sampling method for matting diff --git a/modules/ximgproc/include/opencv2/ximgproc.hpp b/modules/ximgproc/include/opencv2/ximgproc.hpp index 4632f53127a..32330435b45 100644 --- a/modules/ximgproc/include/opencv2/ximgproc.hpp +++ b/modules/ximgproc/include/opencv2/ximgproc.hpp @@ -59,6 +59,7 @@ #include "ximgproc/run_length_morphology.hpp" #include "ximgproc/edgepreserving_filter.hpp" #include "ximgproc/color_match.hpp" +#include "ximgproc/globalmatting.hpp" /** @defgroup ximgproc Extended Image Processing diff --git a/modules/ximgproc/include/opencv2/ximgproc/globalmatting.hpp b/modules/ximgproc/include/opencv2/ximgproc/globalmatting.hpp new file mode 100644 index 00000000000..5b4cd160888 --- /dev/null +++ b/modules/ximgproc/include/opencv2/ximgproc/globalmatting.hpp @@ -0,0 +1,86 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +#ifndef GLOBAL_MATTING_H +#define GLOBAL_MATTING_H + +#include +#include +#include + +#include +#include + + + + +namespace cv +{ + +namespace ximgproc +{ +class CV_EXPORTS GlobalMatting +{ + private: + + std::vector findBoundaryPixels(const cv::Mat_ &trimap, int a, int b); + + // Eq. 2 + float calculateAlpha(const cv::Vec3b &F, const cv::Vec3b &B, const cv::Vec3b &I); + + // Eq. 3 + float colorCost(const cv::Vec3b &F, const cv::Vec3b &B, const cv::Vec3b &I, float alpha); + + // Eq. 4 + float distCost(const cv::Point &p0, const cv::Point &p1, float minDist); + + float colorDist(const cv::Vec3b &I0, const cv::Vec3b &I1); + float nearestDistance(const std::vector &boundary, const cv::Point &p); + + + + + void expansionOfKnownRegions(const cv::Mat_ &image, + cv::Mat_ &trimap, + int r, float c); + + // erode foreground and background regions to increase the size of unknown region + void erodeFB(cv::Mat_ &trimap, int r); + + + + struct Sample + { + int fi, bj; + float df, db; + float cost, alpha; + }; + + void calculateAlphaPatchMatch(const cv::Mat_ &image, + const cv::Mat_ &trimap, + const std::vector &foregroundBoundary, + const std::vector &backgroundBoundary, + std::vector > &samples); + + void expansionOfKnownRegionsHelper(const cv::Mat &_image, + cv::Mat &_trimap, + int r, float c); + + + // erode foreground and background regions to increase the size of unknown region + void erodeFB(cv::Mat &_trimap, int r); + + void expansionOfKnownRegions(cv::InputArray _img, cv::InputOutputArray _trimap, int niter); + void globalMattingHelper(cv::Mat _image, cv::Mat _trimap, cv::Mat &_foreground, cv::Mat &_alpha, cv::Mat &_conf); + public: + GlobalMatting(); + + void globalMatting(cv::InputArray _image, cv::InputArray _trimap, cv::OutputArray _foreground, cv::OutputArray _alpha, cv::OutputArray _conf); + + void getMat(cv::Mat image,cv::Mat trimap,cv::Mat &foreground,cv:: Mat &alpha,int niter=9); + +}; + +} +} +#endif diff --git a/modules/ximgproc/samples/globalmatting.cpp b/modules/ximgproc/samples/globalmatting.cpp new file mode 100644 index 00000000000..564633acbc0 --- /dev/null +++ b/modules/ximgproc/samples/globalmatting.cpp @@ -0,0 +1,48 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +#include +#include +#include +#include + +using namespace std; +using namespace cv; +using namespace ximgproc; +int main(int argc,char** argv) +{ + if(argc<3) + { + cout<<"arg1: Directory of Input image"< +inline T sqr(T a) +{ + return a * a; +} + +struct IntensityComp +{ + IntensityComp(const cv::Mat_ &img_temp) : img(img_temp) + { + + } + + bool operator()(const cv::Point &p0, const cv::Point &p1) const + { + const cv::Vec3b &c0 = img(p0.y, p0.x); + const cv::Vec3b &c1 = img(p1.y, p1.x); + + return ((int)c0[0] + (int)c0[1] + (int)c0[2]) < ((int)c1[0] + (int)c1[1] + (int)c1[2]); + } + + const cv::Mat_ &img; +}; + + + + + +std::vector GlobalMatting::findBoundaryPixels(const cv::Mat_ &trimap, int a, int b) +{ + std::vector result; + + for (int x = 1; x < trimap.cols - 1; ++x) + for (int y = 1; y < trimap.rows - 1; ++y) + { + if (trimap(y, x) == a) + { + if (trimap(y - 1, x) == b || + trimap(y + 1, x) == b || + trimap(y, x - 1) == b || + trimap(y, x + 1) == b) + { + result.push_back(cv::Point(x, y)); + } + } + } + + return result; +} + +// Eq. 2 +float GlobalMatting::calculateAlpha(const cv::Vec3b &F, const cv::Vec3b &B, const cv::Vec3b &I) +{ + float result = 0; + float div = 1e-6f; + for (int c = 0; c < 3; ++c) + { + float f = F[c]; + float b = B[c]; + float i = I[c]; + + result += (i - b) * (f - b); + div += (f - b) * (f - b); + } + + return std::min(std::max(result / div, 0.f), 1.f); +} + +// Eq. 3 +float GlobalMatting::colorCost(const cv::Vec3b &F, const cv::Vec3b &B, const cv::Vec3b &I, float alpha) +{ + float result = 0; + for (int c = 0; c < 3; ++c) + { + float f = F[c]; + float b = B[c]; + float i = I[c]; + + result += sqr(i - (alpha * f + (1 - alpha) * b)); + } + + return sqrt(result); +} + +// Eq. 4 +float GlobalMatting::distCost(const cv::Point &p0, const cv::Point &p1, float minDist) +{ + int dist = sqr(p0.x - p1.x) + sqr(p0.y - p1.y); + return sqrt((float)dist) / minDist; +} + +float GlobalMatting::colorDist(const cv::Vec3b &I0, const cv::Vec3b &I1) +{ + int result = 0; + + for (int c = 0; c < 3; ++c) + result += sqr((int)I0[c] - (int)I1[c]); + + return sqrt((float)result); +} + +float GlobalMatting::nearestDistance(const std::vector &boundary, const cv::Point &p) +{ + int minDist2 = INT_MAX; + for (std::size_t i = 0; i < boundary.size(); ++i) + { + int dist2 = sqr(boundary[i].x - p.x) + sqr(boundary[i].y - p.y); + minDist2 = std::min(minDist2, dist2); + } + + return sqrt((float)minDist2); +} + + + + +void GlobalMatting::expansionOfKnownRegions(const cv::Mat_ &image, + cv::Mat_ &trimap, + int r, float c) +{ + int w = image.cols; + int h = image.rows; + + for (int x = 0; x < w; ++x) + for (int y = 0; y < h; ++y) + { + if (trimap(y, x) != 128) + continue; + + const cv::Vec3b &I = image(y, x); + + for (int j = y-r; j <= y+r; ++j) + for (int i = x-r; i <= x+r; ++i) + { + if (i < 0 || i >= w || j < 0 || j >= h) + continue; + + if (trimap(j, i) != 0 && trimap(j, i) != 255) + continue; + + const cv::Vec3b &I2 = image(j, i); + + float pd = sqrt((float)(sqr(x - i) + sqr(y - j))); + float cd = colorDist(I, I2); + + if (pd <= r && cd <= c) + { + if (trimap(j, i) == 0) + trimap(y, x) = 1; + else if (trimap(j, i) == 255) + trimap(y, x) = 254; + } + } + } + + for (int x = 0; x < trimap.cols; ++x) + for (int y = 0; y < trimap.rows; ++y) + { + if (trimap(y, x) == 1) + trimap(y, x) = 0; + else if (trimap(y, x) == 254) + trimap(y, x) = 255; + + } +} + +// erode foreground and background regions to increase the size of unknown region +void GlobalMatting::erodeFB(cv::Mat_ &trimap, int r) +{ + int w = trimap.cols; + int h = trimap.rows; + + cv::Mat_ foreground(trimap.size(), (uchar)0); + cv::Mat_ background(trimap.size(), (uchar)0); + + for (int y = 0; y < h; ++y) + for (int x = 0; x < w; ++x) + { + if (trimap(y, x) == 0) + background(y, x) = 1; + else if (trimap(y, x) == 255) + foreground(y, x) = 1; + } + + + cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(r, r)); + + cv::erode(background, background, kernel); + cv::erode(foreground, foreground, kernel); + + for (int y = 0; y < h; ++y) + for (int x = 0; x < w; ++x) + { + if (background(y, x) == 0 && foreground(y, x) == 0) + trimap(y, x) = 128; + } +} + + +void GlobalMatting::calculateAlphaPatchMatch(const cv::Mat_ &image, + const cv::Mat_ &trimap, + const std::vector &foregroundBoundary, + const std::vector &backgroundBoundary, + std::vector > &samples) +{ + int w = image.cols; + int h = image.rows; + + samples.resize(h, std::vector(w)); + + for (int y = 0; y < h; ++y) + for (int x = 0; x < w; ++x) + { + if (trimap(y, x) == 128) + { + cv::Point p(x, y); + + samples[y][x].fi = rand() % foregroundBoundary.size(); + samples[y][x].bj = rand() % backgroundBoundary.size(); + samples[y][x].df = nearestDistance(foregroundBoundary, p); + samples[y][x].db = nearestDistance(backgroundBoundary, p); + samples[y][x].cost = FLT_MAX; + } + } + + std::vector coords(w * h); + for (int y = 0; y < h; ++y) + for (int x = 0; x < w; ++x) + coords[x + y * w] = cv::Point(x, y); + + for (int iter = 0; iter < 10; ++iter) + { + // propagation + std::random_shuffle(coords.begin(), coords.end()); + + for (std::size_t i = 0; i < coords.size(); ++i) + { + const cv::Point &p = coords[i]; + + int x = p.x; + int y = p.y; + + if (trimap(y, x) != 128) + continue; + + const cv::Vec3b &I = image(y, x); + + Sample &s = samples[y][x]; + + for (int y2 = y - 1; y2 <= y + 1; ++y2) + for (int x2 = x - 1; x2 <= x + 1; ++x2) + { + if (x2 < 0 || x2 >= w || y2 < 0 || y2 >= h) + continue; + + if (trimap(y2, x2) != 128) + continue; + + Sample &s2 = samples[y2][x2]; + + const cv::Point &fp = foregroundBoundary[s2.fi]; + const cv::Point &bp = backgroundBoundary[s2.bj]; + + const cv::Vec3b F = image(fp.y, fp.x); + const cv::Vec3b B = image(bp.y, bp.x); + + float alpha = calculateAlpha(F, B, I); + + float cost = colorCost(F, B, I, alpha) + distCost(p, fp, s.df) + distCost(p, bp, s.db); + + if (cost < s.cost) + { + s.fi = s2.fi; + s.bj = s2.bj; + s.cost = cost; + s.alpha = alpha; + } + } + } + + // random walk + int w2 = (int)std::max(foregroundBoundary.size(), backgroundBoundary.size()); + + for (int y = 0; y < h; ++y) + for (int x = 0; x < w; ++x) + { + if (trimap(y, x) != 128) + continue; + + cv::Point p(x, y); + + const cv::Vec3b &I = image(y, x); + + Sample &s = samples[y][x]; + + for (int k = 0; ; k++) + { + float r = w2 * pow(0.5f, k); + + if (r < 1) + break; + + int di = int(r * (rand() / (RAND_MAX + 1.f))); + int dj = int(r * (rand() / (RAND_MAX + 1.f))); + + int fi = s.fi + di; + int bj = s.bj + dj; + + if (fi < 0 || (unsigned)fi >= foregroundBoundary.size() || bj < 0 || (unsigned)bj >= backgroundBoundary.size()) + continue; + + const cv::Point &fp = foregroundBoundary[fi]; + const cv::Point &bp = backgroundBoundary[bj]; + + const cv::Vec3b F = image(fp.y, fp.x); + const cv::Vec3b B = image(bp.y, bp.x); + + float alpha = calculateAlpha(F, B, I); + + float cost = colorCost(F, B, I, alpha) + distCost(p, fp, s.df) + distCost(p, bp, s.db); + + if (cost < s.cost) + { + s.fi = fi; + s.bj = bj; + s.cost = cost; + s.alpha = alpha; + } + } + } + } +} + +void GlobalMatting::expansionOfKnownRegionsHelper(const cv::Mat &_image, + cv::Mat &_trimap, + int r, float c) +{ + const cv::Mat_ &image = (const cv::Mat_ &)_image; + cv::Mat_ &trimap = (cv::Mat_&)_trimap; + + int w = image.cols; + int h = image.rows; + + for (int y = 0; y < h; ++y) + for (int x = 0; x < w; ++x) + { + if (trimap(y, x) != 128) + continue; + + const cv::Vec3b &I = image(y, x); + + for (int j = y-r; j <= y+r; ++j) + for (int i = x-r; i <= x+r; ++i) + { + if (i < 0 || i >= w || j < 0 || j >= h) + continue; + + if (trimap(j, i) != 0 && trimap(j, i) != 255) + continue; + + const cv::Vec3b &I2 = image(j, i); + + float pd = sqrt((float)(sqr(x - i) + sqr(y - j))); + float cd = colorDist(I, I2); + + if (pd <= r && cd <= c) + { + if (trimap(j, i) == 0) + trimap(y, x) = 1; + else if (trimap(j, i) == 255) + trimap(y, x) = 254; + } + } + } + + for (int y = 0; y < trimap.rows; ++y) + for (int x = 0; x < trimap.cols; ++x) + { + if (trimap(y, x) == 1) + trimap(y, x) = 0; + else if (trimap(y, x) == 254) + trimap(y, x) = 255; + + } +} + +// erode foreground and background regions to increase the size of unknown region +void GlobalMatting::erodeFB(cv::Mat &_trimap, int r) +{ + cv::Mat_ &trimap = (cv::Mat_&)_trimap; + + int w = trimap.cols; + int h = trimap.rows; + + cv::Mat_ foreground(trimap.size(), (uchar)0); + cv::Mat_ background(trimap.size(), (uchar)0); + + for (int y = 0; y < h; ++y) + for (int x = 0; x < w; ++x) + { + if (trimap(y, x) == 0) + background(y, x) = 1; + else if (trimap(y, x) == 255) + foreground(y, x) = 1; + } + + + cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(r, r)); + + cv::erode(background, background, kernel); + cv::erode(foreground, foreground, kernel); + + for (int y = 0; y < h; ++y) + for (int x = 0; x < w; ++x) + { + if (background(y, x) == 0 && foreground(y, x) == 0) + trimap(y, x) = 128; + } +} + +void GlobalMatting::expansionOfKnownRegions(cv::InputArray _img, cv::InputOutputArray _trimap, int niter) +{ + cv::Mat img = _img.getMat(); + cv::Mat &trimap = _trimap.getMatRef(); + + if (img.empty()) + CV_Error(CV_StsBadArg, "image is empty"); + if (img.type() != CV_8UC3) + CV_Error(CV_StsBadArg, "image mush have CV_8UC3 type"); + + if (trimap.empty()) + CV_Error(CV_StsBadArg, "trimap is empty"); + if (trimap.type() != CV_8UC1) + CV_Error(CV_StsBadArg, "trimap mush have CV_8UC1 type"); + + if (img.size() != trimap.size()) + CV_Error(CV_StsBadArg, "image and trimap mush have same size"); + + for (int i = 0; i < niter; ++i) + expansionOfKnownRegionsHelper(img, trimap, i + 1, float(niter - i)); + erodeFB(trimap, 2); +} + + +void GlobalMatting::globalMattingHelper(cv::Mat _image, cv::Mat _trimap, cv::Mat &_foreground, cv::Mat &_alpha, cv::Mat &_conf) +{ + const cv::Mat_ &image = (const cv::Mat_&)_image; + const cv::Mat_ &trimap = (const cv::Mat_&)_trimap; + + std::vector foregroundBoundary = findBoundaryPixels(trimap, 255, 128); + std::vector backgroundBoundary = findBoundaryPixels(trimap, 0, 128); + + int n = (int)(foregroundBoundary.size() + backgroundBoundary.size()); + for (int i = 0; i < n; ++i) + { + int x = rand() % trimap.cols; + int y = rand() % trimap.rows; + + if (trimap(y, x) == 0) + backgroundBoundary.push_back(cv::Point(x, y)); + else if (trimap(y, x) == 255) + foregroundBoundary.push_back(cv::Point(x, y)); + } + + std::sort(foregroundBoundary.begin(), foregroundBoundary.end(), IntensityComp(image)); + std::sort(backgroundBoundary.begin(), backgroundBoundary.end(), IntensityComp(image)); + + std::vector > samples; + calculateAlphaPatchMatch(image, trimap, foregroundBoundary, backgroundBoundary, samples); + + _foreground.create(image.size(), CV_8UC3); + _alpha.create(image.size(), CV_8UC1); + _conf.create(image.size(), CV_8UC1); + + cv::Mat_ &foreground = (cv::Mat_&)_foreground; + cv::Mat_ &alpha = (cv::Mat_&)_alpha; + cv::Mat_ &conf = (cv::Mat_&)_conf; + + for (int y = 0; y < alpha.rows; ++y) + for (int x = 0; x < alpha.cols; ++x) + { + switch (trimap(y, x)) + { + case 0: + alpha(y, x) = 0; + conf(y, x) = 255; + foreground(y, x) = 0; + break; + case 128: + { + alpha(y, x) = uchar(255 * samples[y][x].alpha); + conf(y, x) = uchar(255 * exp(-samples[y][x].cost / 6)); + cv::Point p = foregroundBoundary[samples[y][x].fi]; + foreground(y, x) = image(p.y, p.x); + break; + } + case 255: + alpha(y, x) = 255; + conf(y, x) = 255; + foreground(y, x) = image(y, x); + break; + } + } +} + +void GlobalMatting::globalMatting(cv::InputArray _image, cv::InputArray _trimap, cv::OutputArray _foreground, cv::OutputArray _alpha, cv::OutputArray _conf) +{ + cv::Mat image = _image.getMat(); + cv::Mat trimap = _trimap.getMat(); + + if (image.empty()) + CV_Error(CV_StsBadArg, "image is empty"); + if (image.type() != CV_8UC3) + CV_Error(CV_StsBadArg, "image mush have CV_8UC3 type"); + + if (trimap.empty()) + CV_Error(CV_StsBadArg, "trimap is empty"); + if (trimap.type() != CV_8UC1) + CV_Error(CV_StsBadArg, "trimap mush have CV_8UC1 type"); + + if (image.size() != trimap.size()) + CV_Error(CV_StsBadArg, "image and trimap mush have same size"); + + cv::Mat &foreground = _foreground.getMatRef(); + cv::Mat &alpha = _alpha.getMatRef(); + cv::Mat tempConf; + + globalMattingHelper(image, trimap, foreground, alpha, tempConf); + + cv::ximgproc::guidedFilter(image,alpha,alpha,10,1e-5); + + if(_conf.needed()) + tempConf.copyTo(_conf); +} + +void GlobalMatting::getMat(cv::Mat image,cv::Mat trimap,cv::Mat &foreground,cv:: Mat &alpha,int niter) +{ + cv::Mat conf; + globalMatting(image,trimap,foreground,alpha,conf); + expansionOfKnownRegions(image,trimap,niter); + for (int x = 0; x < trimap.cols; ++x) + { + for (int y = 0; y < trimap.rows; ++y) + { + if (trimap.at(y, x) == 0) + alpha.at(y, x) = 0; + else if (trimap.at(y, x) == 255) + alpha.at(y, x) = 255; + } + } +} + +GlobalMatting::GlobalMatting() +{ +} + +} +} diff --git a/modules/ximgproc/test/test_globalmatting.cpp b/modules/ximgproc/test/test_globalmatting.cpp new file mode 100644 index 00000000000..0c7d9d2999d --- /dev/null +++ b/modules/ximgproc/test/test_globalmatting.cpp @@ -0,0 +1,84 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +#include "test_precomp.hpp" +using namespace cv; + +namespace opencv_test +{ +namespace +{ +const std::string INPUT_DIR = "cv/ximgproc"; +const std::string IMAGE_FILENAME = "input/doll.png"; +const std::string TRIMAP_FILENAME = "trimap/doll.png"; + + +class CV_GlobalMattingTest +{ + public: + CV_GlobalMattingTest(); + + protected: + Ptr gm; + virtual void run(int); + void runModel(); + +}; + +void CV_GlobalMattingTest::runModel() +{ + std::string folder = std::string(cvtest::TS::ptr()->get_data_path()); + std::string img_path = folder + INPUT_DIR + "/" + IMAGE_FILENAME; + std::string trimap_path = folder + INPUT_DIR + "/" + TRIMAP_FILENAME; + + Mat img = cv::imread(img_path,cv::IMREAD_COLOR); + Mat trimap = cv::imread(trimap_path,cv::IMREAD_GRAYSCALE); + if(img.empty() || trimap.empty()) + { + ts->printf(cvtest::TS::LOG,"Test images not found!\n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + if(img.cols!=trimap.cols || img.rows!=trimap.rows) + { + ts->printf(cvtest::TS::LOG,"Dimensions of trimap and the image are not the same"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + Mat foreground,alpha; + int niter = 9; + this->gm->getMat(img,trimap,foreground,alpha,niter); + if(alpha.empty()) + { + ts->printf(cvtest::TS::LOG,"Could not find the alpha matte for the image\n"); + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + return; + } + + if(alpha.cols!=img.cols || alpha.rows!=img.rows) + { + ts->printf(cvtest::TS::LOG,"The dimensions of the output are not correct"); + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + return; + } +} + +CV_GlobalMattingTest::CV_GlobalMattingTest() +{ + gm = makePtr(); +} +void CV_GlobalMattingTest::run(int) +{ + runModel(); +} + + + +TEST(CV_GlobalMattingTest,accuracy) +{ + CV_GlobalMattingTest test; + test.safe_run(); +} + +} +} diff --git a/modules/ximgproc/tutorials/globalmatting.md b/modules/ximgproc/tutorials/globalmatting.md new file mode 100644 index 00000000000..a7f402ae14e --- /dev/null +++ b/modules/ximgproc/tutorials/globalmatting.md @@ -0,0 +1,31 @@ +### A Global sampling method for Alpha Matting ### + +The Global sampling based method uses all known samples(from a trimap), Unlike other sampling based methods +which collect only nearby samples.
+It first estimates the foreground and background color and then computes the alpha matte. + +Input to the model is the Image and the trimap, and the output is the alpha matte of the image. + +This [blog post](https://medium.com/vedacv/paper-summary-a-global-sampling-method-for-alpha-matting-490a4217eb2) gives a summary of the paper. + +### Results ### + +After evaluating this implementation on alphamatting.com, the results are almost as good as the original implementation. + +Following were the results: + +| Error type | Original implementation | This implementation | +| ----------- | ------------------------ | ------------------- | +| Sum of absolute differences | 31 | 31.3 | +| Mean square error | 28.3 | 29.5 | +| Gradient error | 25 | 26.3 | +| Connectivity error | 28 | 36.3 | + + +Some of the outputs with of this implementation are as follows : + +| Image | Trimap | Alpha matte(this implementation) | +| -------------- | -------------- | ------------------------ | +|![alt text](https://raw.githubusercontent.com/Nerdyvedi/GSOC-Opencv-matting/master/Global-Sampling-Method-for-Alpha-Matting/Input/doll.png "img1" ) |![alt text](https://raw.githubusercontent.com/Nerdyvedi/GSOC-Opencv-matting/master/Global-Sampling-Method-for-Alpha-Matting/Trimap/doll.png "trimap1" ) |![alt text](https://raw.githubusercontent.com/Nerdyvedi/GSOC-Opencv-matting/master/Global-Sampling-Method-for-Alpha-Matting/Results/doll.png "results1" ) | +|![alt text](https://raw.githubusercontent.com/Nerdyvedi/GSOC-Opencv-matting/master/Global-Sampling-Method-for-Alpha-Matting/Input/troll.png "img2" ) |![alt text](https://raw.githubusercontent.com/Nerdyvedi/GSOC-Opencv-matting/master/Global-Sampling-Method-for-Alpha-Matting/Trimap/troll.png "trimap2" ) |![alt text](https://raw.githubusercontent.com/Nerdyvedi/GSOC-Opencv-matting/master/Global-Sampling-Method-for-Alpha-Matting/Results/troll.png "results2" ) | +|![alt text](https://raw.githubusercontent.com/Nerdyvedi/GSOC-Opencv-matting/master/Global-Sampling-Method-for-Alpha-Matting/Input/donkey.png "img1" ) |![alt text](https://raw.githubusercontent.com/Nerdyvedi/GSOC-Opencv-matting/master/Global-Sampling-Method-for-Alpha-Matting/Trimap/donkey.png "trimap1" ) |![alt text](https://raw.githubusercontent.com/Nerdyvedi/GSOC-Opencv-matting/master/Global-Sampling-Method-for-Alpha-Matting/Results/donkey.png "results1" ) |