Merge pull request #1819 from berak:fast_bilateral
The Fast Bilateral Filter (#1819) PR is created by: berak <px1704@web.de> based on work https://github.com/opencv/opencv_contrib/pull/1317 * fbs_filter v1.0 has been contributed * use boost unordered_map * add brief description for fbs_filter * fix format * fix channels bug * modify doc for fbs_filter * check c++ 11 * asDiagonal -> diagonal * rosolve warning * fix eigen3 dependency * Eigen/Core * test HEAV_EIGEN * setZero bug * unordered_map test * fix macro bug * fix boost not found * fix eigen macro bug * fix eigen macro bug * fix eigen macro bug * fix eigen macro bug * add test file * fix test macro * fix test macro * add test * add test * add sample colorize * fix macro * fix colorize.cpp * fix colorize.cpp * fix colorize.cpp * fix colorize.cpp * add fbs filter demo * add fbs filter demo * add fbs filter demo * use fgsfilter for guess * add parameter num_iter and max_tol * add a option for colorize sample * add bibtex * add bibtex * fix a colorize demo bug * size optimize * taking over the fast bilateral solver * taking over the fast bilateral solver * try to fix test_fbs_filter * missed a bib bracketpull/1835/head
parent
42e0136592
commit
78ad10ad8a
7 changed files with 1318 additions and 7 deletions
@ -0,0 +1,417 @@ |
||||
|
||||
#include "opencv2/core.hpp" |
||||
#include "opencv2/highgui.hpp" |
||||
#include "opencv2/imgproc.hpp" |
||||
#include "opencv2/core/utility.hpp" |
||||
|
||||
#include <time.h> |
||||
#include <vector> |
||||
#include <iostream> |
||||
#include <opencv2/ximgproc.hpp> |
||||
|
||||
|
||||
|
||||
using namespace cv; |
||||
|
||||
#ifdef HAVE_EIGEN |
||||
|
||||
#define MARK_RADIUS 5 |
||||
#define PALLET_RADIUS 100 |
||||
int max_width = 1280; |
||||
int max_height = 720; |
||||
|
||||
static int globalMouseX; |
||||
static int globalMouseY; |
||||
static int selected_r; |
||||
static int selected_g; |
||||
static int selected_b; |
||||
static bool globalMouseClick = false; |
||||
static bool glb_mouse_left = false; |
||||
static bool drawByReference = false; |
||||
static bool mouseDraw = false; |
||||
static bool mouseClick; |
||||
static bool mouseLeft; |
||||
static int mouseX; |
||||
static int mouseY; |
||||
|
||||
cv::Mat mat_draw; |
||||
cv::Mat mat_input_gray; |
||||
cv::Mat mat_input_reference; |
||||
cv::Mat mat_input_confidence; |
||||
cv::Mat mat_pallet(PALLET_RADIUS*2,PALLET_RADIUS*2,CV_8UC3); |
||||
|
||||
|
||||
static void mouseCallback(int event, int x, int y, int flags, void* param); |
||||
void drawTrajectoryByReference(cv::Mat& img); |
||||
double module(Point pt); |
||||
double distance(Point pt1, Point pt2); |
||||
double cross(Point pt1, Point pt2); |
||||
double angle(Point pt1, Point pt2); |
||||
int inCircle(Point p, Point c, int r); |
||||
void createPlate(Mat &im1, int radius); |
||||
|
||||
|
||||
#endif |
||||
|
||||
const String keys = |
||||
"{help h usage ? | | print this message }" |
||||
"{@image | | input image }" |
||||
"{sigma_spatial |8 | parameter of post-filtering }" |
||||
"{sigma_luma |8 | parameter of post-filtering }" |
||||
"{sigma_chroma |8 | parameter of post-filtering }" |
||||
"{dst_path |None | optional path to save the resulting colorized image }" |
||||
"{dst_raw_path |None | optional path to save drawed image before filtering }" |
||||
"{draw_by_reference |false | optional flag to use color image as reference }" |
||||
; |
||||
|
||||
|
||||
|
||||
int main(int argc, char* argv[]) |
||||
{ |
||||
|
||||
CommandLineParser parser(argc,argv,keys); |
||||
parser.about("fastBilateralSolverFilter Demo"); |
||||
if (parser.has("help")) |
||||
{ |
||||
parser.printMessage(); |
||||
return 0; |
||||
} |
||||
|
||||
#ifdef HAVE_EIGEN |
||||
|
||||
String img = parser.get<String>(0); |
||||
double sigma_spatial = parser.get<double>("sigma_spatial"); |
||||
double sigma_luma = parser.get<double>("sigma_luma"); |
||||
double sigma_chroma = parser.get<double>("sigma_chroma"); |
||||
String dst_path = parser.get<String>("dst_path"); |
||||
String dst_raw_path = parser.get<String>("dst_raw_path"); |
||||
drawByReference = parser.get<bool>("draw_by_reference"); |
||||
|
||||
mat_input_reference = cv::imread(img, IMREAD_COLOR); |
||||
if (mat_input_reference.empty()) |
||||
{ |
||||
std::cerr << "input image '" << img << "' could not be read !" << std::endl << std::endl; |
||||
parser.printMessage(); |
||||
return 1; |
||||
} |
||||
|
||||
cvtColor(mat_input_reference, mat_input_gray, COLOR_BGR2GRAY); |
||||
|
||||
if(mat_input_gray.cols > max_width) |
||||
{ |
||||
double scale = float(max_width) / float(mat_input_gray.cols); |
||||
cv::resize(mat_input_reference, mat_input_reference, cv::Size(), scale, scale); |
||||
cv::resize(mat_input_gray, mat_input_gray, cv::Size(), scale, scale); |
||||
} |
||||
|
||||
if(mat_input_gray.rows > max_height) |
||||
{ |
||||
double scale = float(max_height) / float(mat_input_gray.rows); |
||||
cv::resize(mat_input_reference, mat_input_reference, cv::Size(), scale, scale); |
||||
cv::resize(mat_input_gray, mat_input_gray, cv::Size(), scale, scale); |
||||
} |
||||
|
||||
|
||||
float filtering_time; |
||||
std::cout << "mat_input_reference:" << mat_input_reference.cols<<"x"<< mat_input_reference.rows<< std::endl; |
||||
std::cout << "please select a color from the palette, by clicking into that," << std::endl; |
||||
std::cout << " then select a coarse region in the image to be coloured." << std::endl; |
||||
std::cout << " press 'escape' to see the final coloured image." << std::endl; |
||||
|
||||
|
||||
cv::Mat mat_gray; |
||||
cv::cvtColor(mat_input_reference, mat_gray, cv::COLOR_BGR2GRAY); |
||||
|
||||
cv::Mat target = mat_input_reference.clone(); |
||||
cvtColor(mat_gray, mat_input_reference, COLOR_GRAY2BGR); |
||||
|
||||
cv::namedWindow("draw", cv::WINDOW_AUTOSIZE); |
||||
|
||||
// construct pallet
|
||||
createPlate(mat_pallet, PALLET_RADIUS); |
||||
selected_b = 0; |
||||
selected_g = 0; |
||||
selected_r = 0; |
||||
|
||||
cv::Mat mat_show(target.rows,target.cols+PALLET_RADIUS*2,CV_8UC3); |
||||
cv::Mat color_select(target.rows-mat_pallet.rows,PALLET_RADIUS*2,CV_8UC3,cv::Scalar(selected_b, selected_g, selected_r)); |
||||
target.copyTo(Mat(mat_show,Rect(0,0,target.cols,target.rows))); |
||||
mat_pallet.copyTo(Mat(mat_show,Rect(target.cols,0,mat_pallet.cols,mat_pallet.rows))); |
||||
color_select.copyTo(Mat(mat_show,Rect(target.cols,PALLET_RADIUS*2,color_select.cols,color_select.rows))); |
||||
|
||||
cv::imshow("draw", mat_show); |
||||
cv::setMouseCallback("draw", mouseCallback, (void *)&mat_show); |
||||
mat_input_confidence = 0*cv::Mat::ones(mat_gray.size(),mat_gray.type()); |
||||
|
||||
int show_count = 0; |
||||
while (1) |
||||
{ |
||||
mouseX = globalMouseX; |
||||
mouseY = globalMouseY; |
||||
mouseClick = globalMouseClick; |
||||
mouseLeft = glb_mouse_left; |
||||
|
||||
|
||||
if (mouseClick) |
||||
{ |
||||
drawTrajectoryByReference(target); |
||||
|
||||
if(show_count%5==0) |
||||
{ |
||||
cv::Mat target_temp(target.size(),target.type()); |
||||
filtering_time = (double)getTickCount(); |
||||
if(mouseDraw) |
||||
{ |
||||
cv::cvtColor(target, target_temp, cv::COLOR_BGR2YCrCb); |
||||
std::vector<cv::Mat> src_channels; |
||||
std::vector<cv::Mat> dst_channels; |
||||
|
||||
cv::split(target_temp,src_channels); |
||||
|
||||
cv::Mat result1 = cv::Mat(mat_input_gray.size(),mat_input_gray.type()); |
||||
cv::Mat result2 = cv::Mat(mat_input_gray.size(),mat_input_gray.type()); |
||||
|
||||
dst_channels.push_back(mat_input_gray); |
||||
cv::ximgproc::fastBilateralSolverFilter(mat_input_gray,src_channels[1],mat_input_confidence,result1,sigma_spatial,sigma_luma,sigma_chroma); |
||||
dst_channels.push_back(result1); |
||||
cv::ximgproc::fastBilateralSolverFilter(mat_input_gray,src_channels[2],mat_input_confidence,result2,sigma_spatial,sigma_luma,sigma_chroma); |
||||
dst_channels.push_back(result2); |
||||
|
||||
cv::merge(dst_channels,target_temp); |
||||
cv::cvtColor(target_temp, target_temp, cv::COLOR_YCrCb2BGR); |
||||
} |
||||
else |
||||
{ |
||||
target_temp = target.clone(); |
||||
} |
||||
filtering_time = ((double)getTickCount() - filtering_time)/getTickFrequency(); |
||||
std::cout << "solver time: " << filtering_time << "s" << std::endl; |
||||
|
||||
cv::Mat color_selected(target_temp.rows-mat_pallet.rows,PALLET_RADIUS*2,CV_8UC3,cv::Scalar(selected_b, selected_g, selected_r)); |
||||
target_temp.copyTo(Mat(mat_show,Rect(0,0,target_temp.cols,target_temp.rows))); |
||||
mat_pallet.copyTo(Mat(mat_show,Rect(target_temp.cols,0,mat_pallet.cols,mat_pallet.rows))); |
||||
color_selected.copyTo(Mat(mat_show,Rect(target_temp.cols,PALLET_RADIUS*2,color_selected.cols,color_selected.rows))); |
||||
cv::imshow("draw", mat_show); |
||||
} |
||||
show_count++; |
||||
} |
||||
if (cv::waitKey(2) == 27) |
||||
break; |
||||
} |
||||
mat_draw = target.clone(); |
||||
cv::cvtColor(target, target, cv::COLOR_BGR2YCrCb); |
||||
|
||||
std::vector<cv::Mat> src_channels; |
||||
std::vector<cv::Mat> dst_channels; |
||||
|
||||
cv::split(target,src_channels); |
||||
|
||||
cv::Mat result1 = cv::Mat(mat_input_gray.size(),mat_input_gray.type()); |
||||
cv::Mat result2 = cv::Mat(mat_input_gray.size(),mat_input_gray.type()); |
||||
|
||||
filtering_time = (double)getTickCount(); |
||||
|
||||
// dst_channels.push_back(src_channels[0]);
|
||||
dst_channels.push_back(mat_input_gray); |
||||
cv::ximgproc::fastBilateralSolverFilter(mat_input_gray,src_channels[1],mat_input_confidence,result1,sigma_spatial,sigma_luma,sigma_chroma); |
||||
dst_channels.push_back(result1); |
||||
cv::ximgproc::fastBilateralSolverFilter(mat_input_gray,src_channels[2],mat_input_confidence,result2,sigma_spatial,sigma_luma,sigma_chroma); |
||||
dst_channels.push_back(result2); |
||||
|
||||
cv::merge(dst_channels,target); |
||||
cv::cvtColor(target, target, cv::COLOR_YCrCb2BGR); |
||||
|
||||
filtering_time = ((double)getTickCount() - filtering_time)/getTickFrequency(); |
||||
std::cout << "solver time: " << filtering_time << "s" << std::endl; |
||||
|
||||
|
||||
|
||||
cv::imshow("mat_draw",mat_draw); |
||||
cv::imshow("output",target); |
||||
|
||||
if(dst_path!="None") |
||||
{ |
||||
imwrite(dst_path,target); |
||||
} |
||||
if(dst_raw_path!="None") |
||||
{ |
||||
imwrite(dst_raw_path,mat_draw); |
||||
} |
||||
|
||||
cv::waitKey(0); |
||||
|
||||
|
||||
|
||||
#else |
||||
std::cout << "Can not find eigen, please build with eigen by set WITH_EIGEN=ON" << '\n'; |
||||
#endif |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
|
||||
#ifdef HAVE_EIGEN |
||||
static void mouseCallback(int event, int x, int y, int, void*) |
||||
{ |
||||
switch (event) |
||||
{ |
||||
case cv::EVENT_MOUSEMOVE: |
||||
if (globalMouseClick) |
||||
{ |
||||
globalMouseX = x; |
||||
globalMouseY = y; |
||||
} |
||||
break; |
||||
|
||||
case cv::EVENT_LBUTTONDOWN: |
||||
globalMouseClick = true; |
||||
globalMouseX = x; |
||||
globalMouseY = y; |
||||
break; |
||||
|
||||
case cv::EVENT_LBUTTONUP: |
||||
glb_mouse_left = true; |
||||
globalMouseClick = false; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
void drawTrajectoryByReference(cv::Mat& img) |
||||
{ |
||||
int i, j; |
||||
uchar red, green, blue; |
||||
float gray; |
||||
int y, x; |
||||
int r = MARK_RADIUS; |
||||
int r2 = r * r; |
||||
uchar* colorPix; |
||||
uchar* grayPix; |
||||
|
||||
if(mouseY < PALLET_RADIUS*2 && img.cols <= mouseX && mouseX < img.cols+PALLET_RADIUS*2) |
||||
{ |
||||
colorPix = mat_pallet.ptr<uchar>(mouseY, mouseX - img.cols); |
||||
// colorPix = mat_pallet.ptr<uchar>(mouseY, mouseX);
|
||||
selected_b = *colorPix; |
||||
colorPix++; |
||||
selected_g = *colorPix; |
||||
colorPix++; |
||||
selected_r = *colorPix; |
||||
colorPix++; |
||||
std::cout << "x y:("<<mouseX<<"," <<mouseY<< " rgb_select:("<< selected_r<<","<<selected_g<<","<<selected_b<<")" << '\n'; |
||||
} |
||||
else |
||||
{ |
||||
mouseDraw = true; |
||||
y = mouseY - r; |
||||
for(i=-r; i<r+1 ; i++, y++) |
||||
{ |
||||
x = mouseX - r; |
||||
colorPix = mat_input_reference.ptr<uchar>(y, x); |
||||
grayPix = mat_input_gray.ptr<uchar>(y, x); |
||||
for(j=-r; j<r+1; j++, x++) |
||||
{ |
||||
if(i*i + j*j > r2) |
||||
{ |
||||
colorPix += mat_input_reference.channels(); |
||||
grayPix += mat_input_gray.channels(); |
||||
continue; |
||||
} |
||||
|
||||
if(y<0 || y>=mat_input_reference.rows || x<0 || x>=mat_input_reference.cols) |
||||
{ |
||||
break; |
||||
} |
||||
|
||||
blue = *colorPix; |
||||
colorPix++; |
||||
green = *colorPix; |
||||
colorPix++; |
||||
red = *colorPix; |
||||
colorPix++; |
||||
gray = *grayPix; |
||||
grayPix++; |
||||
mat_input_confidence.at<uchar>(y,x) = 255; |
||||
float draw_y = 0.229*(float(selected_r)) + 0.587*(float(selected_g)) + 0.114*(float(selected_b)); |
||||
int draw_b = int(float(selected_b)*(gray/draw_y)); |
||||
int draw_g = int(float(selected_g)*(gray/draw_y)); |
||||
int draw_r = int(float(selected_r)*(gray/draw_y)); |
||||
|
||||
if(drawByReference) |
||||
{ |
||||
cv::circle(img, cv::Point2d(x, y), 0.1, cv::Scalar(blue, green, red), -1); |
||||
} |
||||
else |
||||
{ |
||||
cv::circle(img, cv::Point2d(x, y), 0.1, cv::Scalar(draw_b, draw_g, draw_r), -1); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
double module(Point pt) |
||||
{ |
||||
return sqrt((double)pt.x*pt.x + pt.y*pt.y); |
||||
} |
||||
|
||||
double distance(Point pt1, Point pt2) |
||||
{ |
||||
int dx = pt1.x - pt2.x; |
||||
int dy = pt1.y - pt2.y; |
||||
return sqrt((double)dx*dx + dy*dy); |
||||
} |
||||
|
||||
double cross(Point pt1, Point pt2) |
||||
{ |
||||
return pt1.x*pt2.x + pt1.y*pt2.y; |
||||
} |
||||
|
||||
double angle(Point pt1, Point pt2) |
||||
{ |
||||
return acos(cross(pt1, pt2) / (module(pt1)*module(pt2) + DBL_EPSILON)); |
||||
} |
||||
|
||||
// p or c is the center
|
||||
int inCircle(Point p, Point c, int r) |
||||
{ |
||||
int dx = p.x - c.x; |
||||
int dy = p.y - c.y; |
||||
return dx*dx + dy*dy <= r*r ? 1 : 0; |
||||
|
||||
} |
||||
|
||||
//draw the hsv-plate
|
||||
void createPlate(Mat &im1, int radius) |
||||
{ |
||||
Mat hsvImag(Size(radius << 1, radius << 1), CV_8UC3, Scalar(0, 0, 255)); |
||||
int w = hsvImag.cols; |
||||
int h = hsvImag.rows; |
||||
int cx = w >> 1; |
||||
int cy = h >> 1; |
||||
Point pt1(cx, 0); |
||||
|
||||
for (int j = 0; j < w; j++) |
||||
{ |
||||
for (int i = 0; i < h; i++) |
||||
{ |
||||
Point pt2(j - cx, i - cy); |
||||
if (inCircle(Point(0, 0), pt2, radius)) |
||||
{ |
||||
int theta = angle(pt1, pt2) * 180 / CV_PI; |
||||
if (i > cx) |
||||
{ |
||||
theta = -theta + 360; |
||||
} |
||||
hsvImag.at<Vec3b>(i, j)[0] = theta / 2; |
||||
hsvImag.at<Vec3b>(i, j)[1] = module(pt2) / cx * 255; |
||||
hsvImag.at<Vec3b>(i, j)[2] = 255; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
cvtColor(hsvImag, im1, COLOR_HSV2BGR); |
||||
} |
||||
|
||||
|
||||
#endif |
@ -0,0 +1,593 @@ |
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license. |
||||
* If you do not agree to this license, do not download, install, |
||||
* copy or use the software. |
||||
* |
||||
* |
||||
* License Agreement |
||||
* For Open Source Computer Vision Library |
||||
* (3 - clause BSD License) |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without modification, |
||||
* are permitted provided that the following conditions are met : |
||||
* |
||||
* * Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright notice, |
||||
* this list of conditions and the following disclaimer in the documentation |
||||
* and / or other materials provided with the distribution. |
||||
* |
||||
* * Neither the names of the copyright holders nor the names of the contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* This software is provided by the copyright holders and contributors "as is" and |
||||
* any express or implied warranties, including, but not limited to, the implied |
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed. |
||||
* In no event shall copyright holders or contributors be liable for any direct, |
||||
* indirect, incidental, special, exemplary, or consequential damages |
||||
* (including, but not limited to, procurement of substitute goods or services; |
||||
* loss of use, data, or profits; or business interruption) however caused |
||||
* and on any theory of liability, whether in contract, strict liability, |
||||
* or tort(including negligence or otherwise) arising in any way out of |
||||
* the use of this software, even if advised of the possibility of such damage. |
||||
*/ |
||||
|
||||
#include "precomp.hpp" |
||||
|
||||
#include <cmath> |
||||
#include <vector> |
||||
#include <memory> |
||||
#include <stdlib.h> |
||||
#include <iostream> |
||||
#include <iterator> |
||||
#include <algorithm> |
||||
|
||||
|
||||
#ifdef HAVE_EIGEN |
||||
# if defined __GNUC__ && defined __APPLE__ |
||||
# pragma GCC diagnostic ignored "-Wshadow" |
||||
# endif |
||||
# include <Eigen/Dense> |
||||
# include <Eigen/SparseCore> |
||||
# include <Eigen/SparseCholesky> |
||||
# include <Eigen/IterativeLinearSolvers> |
||||
# include <Eigen/Sparse> |
||||
|
||||
|
||||
|
||||
|
||||
#if __cplusplus <= 199711L |
||||
#include <map> |
||||
typedef std::map<long long /* hash */, int /* vert id */> mapId; |
||||
#else |
||||
#include <unordered_map> |
||||
typedef std::unordered_map<long long /* hash */, int /* vert id */> mapId; |
||||
#endif |
||||
|
||||
namespace cv |
||||
{ |
||||
namespace ximgproc |
||||
{ |
||||
|
||||
class FastBilateralSolverFilterImpl : public FastBilateralSolverFilter |
||||
{ |
||||
public: |
||||
|
||||
static Ptr<FastBilateralSolverFilterImpl> create(InputArray guide, double sigma_spatial, double sigma_luma, double sigma_chroma, int num_iter, double max_tol) |
||||
{ |
||||
CV_Assert(guide.type() == CV_8UC1 || guide.type() == CV_8UC3); |
||||
FastBilateralSolverFilterImpl *fbs = new FastBilateralSolverFilterImpl(); |
||||
Mat gui = guide.getMat(); |
||||
fbs->init(gui,sigma_spatial,sigma_luma,sigma_chroma,num_iter,max_tol); |
||||
return Ptr<FastBilateralSolverFilterImpl>(fbs); |
||||
} |
||||
|
||||
void filter(InputArray src, InputArray confidence, OutputArray dst) CV_OVERRIDE |
||||
{ |
||||
|
||||
CV_Assert(!src.empty() && (src.depth() == CV_8U || src.depth() == CV_16S || src.depth() == CV_32F) && src.channels()<=4); |
||||
CV_Assert(!confidence.empty() && (confidence.depth() == CV_8U || confidence.depth() == CV_16S || confidence.depth() == CV_32F) && confidence.channels()==1); |
||||
if (src.rows() != rows || src.cols() != cols) |
||||
{ |
||||
CV_Error(Error::StsBadSize, "Size of the filtered image must be equal to the size of the guide image"); |
||||
return; |
||||
} |
||||
if (confidence.rows() != rows || confidence.cols() != cols) |
||||
{ |
||||
CV_Error(Error::StsBadSize, "Size of the confidence image must be equal to the size of the guide image"); |
||||
return; |
||||
} |
||||
|
||||
std::vector<Mat> src_channels; |
||||
std::vector<Mat> dst_channels; |
||||
if(src.channels()==1) |
||||
src_channels.push_back(src.getMat()); |
||||
else |
||||
split(src,src_channels); |
||||
|
||||
Mat conf = confidence.getMat(); |
||||
if(conf.depth() != CV_8UC1) |
||||
conf.convertTo(conf, CV_8UC1); |
||||
|
||||
for(int i=0;i<src.channels();i++) |
||||
{ |
||||
Mat cur_res = src_channels[i].clone(); |
||||
if(src.depth() != CV_8UC1) |
||||
cur_res.convertTo(cur_res, CV_8UC1); |
||||
|
||||
solve(cur_res,conf,cur_res); |
||||
cur_res.convertTo(cur_res, src.type()); |
||||
dst_channels.push_back(cur_res); |
||||
} |
||||
|
||||
dst.create(src.size(),src_channels[0].type()); |
||||
if(src.channels()==1) |
||||
{ |
||||
Mat& dstMat = dst.getMatRef(); |
||||
dstMat = dst_channels[0]; |
||||
} |
||||
else |
||||
merge(dst_channels,dst); |
||||
CV_Assert(src.type() == dst.type() && src.size() == dst.size()); |
||||
} |
||||
|
||||
// protected:
|
||||
void solve(cv::Mat& src, cv::Mat& confidence, cv::Mat& dst); |
||||
void init(cv::Mat& reference, double sigma_spatial, double sigma_luma, double sigma_chroma, int num_iter, double max_tol); |
||||
|
||||
void Splat(Eigen::VectorXf& input, Eigen::VectorXf& dst); |
||||
void Blur(Eigen::VectorXf& input, Eigen::VectorXf& dst); |
||||
void Slice(Eigen::VectorXf& input, Eigen::VectorXf& dst); |
||||
|
||||
void diagonal(Eigen::VectorXf& v,Eigen::SparseMatrix<float>& mat) |
||||
{ |
||||
mat = Eigen::SparseMatrix<float>(v.size(),v.size()); |
||||
for (int i = 0; i < int(v.size()); i++) |
||||
{ |
||||
mat.insert(i,i) = v(i); |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
private: |
||||
|
||||
int npixels; |
||||
int nvertices; |
||||
int dim; |
||||
int cols; |
||||
int rows; |
||||
std::vector<int> splat_idx; |
||||
std::vector<std::pair<int, int> > blur_idx; |
||||
Eigen::VectorXf m; |
||||
Eigen::VectorXf n; |
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> blurs; |
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> S; |
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> Dn; |
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> Dm; |
||||
cv::Mat guide; |
||||
|
||||
struct grid_params |
||||
{ |
||||
float spatialSigma; |
||||
float lumaSigma; |
||||
float chromaSigma; |
||||
grid_params() |
||||
{ |
||||
spatialSigma = 8.0; |
||||
lumaSigma = 4.0; |
||||
chromaSigma = 4.0; |
||||
} |
||||
}; |
||||
|
||||
struct bs_params |
||||
{ |
||||
float lam; |
||||
float A_diag_min; |
||||
float cg_tol; |
||||
int cg_maxiter; |
||||
bs_params() |
||||
{ |
||||
lam = 128.0; |
||||
A_diag_min = 1e-5; |
||||
cg_tol = 1e-5; |
||||
cg_maxiter = 25; |
||||
} |
||||
}; |
||||
|
||||
grid_params grid_param; |
||||
bs_params bs_param; |
||||
|
||||
}; |
||||
|
||||
|
||||
|
||||
void FastBilateralSolverFilterImpl::init(cv::Mat& reference, double sigma_spatial, double sigma_luma, double sigma_chroma, int num_iter, double max_tol) |
||||
{ |
||||
guide = reference.clone(); |
||||
|
||||
bs_param.cg_maxiter = num_iter; |
||||
bs_param.cg_tol = max_tol; |
||||
|
||||
if(reference.channels()==1) |
||||
{ |
||||
dim = 3; |
||||
cols = reference.cols; |
||||
rows = reference.rows; |
||||
npixels = cols*rows; |
||||
long long hash_vec[3]; |
||||
for (int i = 0; i < 3; ++i) |
||||
hash_vec[i] = static_cast<long long>(std::pow(255, i)); |
||||
|
||||
mapId hashed_coords; |
||||
#if __cplusplus <= 199711L |
||||
#else |
||||
hashed_coords.reserve(cols*rows); |
||||
#endif |
||||
|
||||
const unsigned char* pref = (const unsigned char*)reference.data; |
||||
int vert_idx = 0; |
||||
int pix_idx = 0; |
||||
|
||||
// construct Splat(Slice) matrices
|
||||
splat_idx.resize(npixels); |
||||
for (int y = 0; y < rows; ++y) |
||||
{ |
||||
for (int x = 0; x < cols; ++x) |
||||
{ |
||||
long long coord[3]; |
||||
coord[0] = int(x / sigma_spatial); |
||||
coord[1] = int(y / sigma_spatial); |
||||
coord[2] = int(pref[0] / sigma_luma); |
||||
|
||||
// convert the coordinate to a hash value
|
||||
long long hash_coord = 0; |
||||
for (int i = 0; i < 3; ++i) |
||||
hash_coord += coord[i] * hash_vec[i]; |
||||
|
||||
// pixels whom are alike will have the same hash value.
|
||||
// We only want to keep a unique list of hash values, therefore make sure we only insert
|
||||
// unique hash values.
|
||||
mapId::iterator it = hashed_coords.find(hash_coord); |
||||
if (it == hashed_coords.end()) |
||||
{ |
||||
hashed_coords.insert(std::pair<long long, int>(hash_coord, vert_idx)); |
||||
splat_idx[pix_idx] = vert_idx; |
||||
++vert_idx; |
||||
} |
||||
else |
||||
{ |
||||
splat_idx[pix_idx] = it->second; |
||||
} |
||||
|
||||
pref += 1; // skip 1 bytes (y)
|
||||
++pix_idx; |
||||
} |
||||
} |
||||
nvertices = hashed_coords.size(); |
||||
|
||||
// construct Blur matrices
|
||||
Eigen::VectorXf ones_nvertices = Eigen::VectorXf::Ones(nvertices); |
||||
Eigen::VectorXf ones_npixels = Eigen::VectorXf::Ones(npixels); |
||||
diagonal(ones_nvertices,blurs); |
||||
blurs *= 10; |
||||
for(int offset = -1; offset <= 1;++offset) |
||||
{ |
||||
if(offset == 0) continue; |
||||
for (int i = 0; i < dim; ++i) |
||||
{ |
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> blur_temp(hashed_coords.size(), hashed_coords.size()); |
||||
blur_temp.reserve(Eigen::VectorXi::Constant(nvertices,6)); |
||||
long long offset_hash_coord = offset * hash_vec[i]; |
||||
for (mapId::iterator it = hashed_coords.begin(); it != hashed_coords.end(); ++it) |
||||
{ |
||||
long long neighb_coord = it->first + offset_hash_coord; |
||||
mapId::iterator it_neighb = hashed_coords.find(neighb_coord); |
||||
if (it_neighb != hashed_coords.end()) |
||||
{ |
||||
blur_temp.insert(it->second,it_neighb->second) = 1.0f; |
||||
blur_idx.push_back(std::pair<int,int>(it->second, it_neighb->second)); |
||||
} |
||||
} |
||||
blurs += blur_temp; |
||||
} |
||||
} |
||||
blurs.finalize(); |
||||
|
||||
//bistochastize
|
||||
int maxiter = 10; |
||||
n = ones_nvertices; |
||||
m = Eigen::VectorXf::Zero(nvertices); |
||||
for (int i = 0; i < int(splat_idx.size()); i++) |
||||
{ |
||||
m(splat_idx[i]) += 1.0f; |
||||
} |
||||
|
||||
Eigen::VectorXf bluredn(nvertices); |
||||
|
||||
for (int i = 0; i < maxiter; i++) |
||||
{ |
||||
Blur(n,bluredn); |
||||
n = ((n.array()*m.array()).array()/bluredn.array()).array().sqrt(); |
||||
} |
||||
Blur(n,bluredn); |
||||
|
||||
m = n.array() * (bluredn).array(); |
||||
diagonal(m,Dm); |
||||
diagonal(n,Dn); |
||||
|
||||
} |
||||
else |
||||
{ |
||||
dim = 5; |
||||
cv::Mat reference_yuv; |
||||
cv::cvtColor(reference, reference_yuv, COLOR_BGR2YCrCb); |
||||
|
||||
cols = reference_yuv.cols; |
||||
rows = reference_yuv.rows; |
||||
npixels = cols*rows; |
||||
long long hash_vec[5]; |
||||
for (int i = 0; i < 5; ++i) |
||||
hash_vec[i] = static_cast<long long>(std::pow(255, i)); |
||||
|
||||
mapId hashed_coords; |
||||
#if __cplusplus <= 199711L |
||||
#else |
||||
hashed_coords.reserve(cols*rows); |
||||
#endif |
||||
|
||||
const unsigned char* pref = (const unsigned char*)reference_yuv.data; |
||||
int vert_idx = 0; |
||||
int pix_idx = 0; |
||||
|
||||
// construct Splat(Slice) matrices
|
||||
splat_idx.resize(npixels); |
||||
for (int y = 0; y < rows; ++y) |
||||
{ |
||||
for (int x = 0; x < cols; ++x) |
||||
{ |
||||
long long coord[5]; |
||||
coord[0] = int(x / sigma_spatial); |
||||
coord[1] = int(y / sigma_spatial); |
||||
coord[2] = int(pref[0] / sigma_luma); |
||||
coord[3] = int(pref[1] / sigma_chroma); |
||||
coord[4] = int(pref[2] / sigma_chroma); |
||||
|
||||
// convert the coordinate to a hash value
|
||||
long long hash_coord = 0; |
||||
for (int i = 0; i < 5; ++i) |
||||
hash_coord += coord[i] * hash_vec[i]; |
||||
|
||||
// pixels whom are alike will have the same hash value.
|
||||
// We only want to keep a unique list of hash values, therefore make sure we only insert
|
||||
// unique hash values.
|
||||
mapId::iterator it = hashed_coords.find(hash_coord); |
||||
if (it == hashed_coords.end()) |
||||
{ |
||||
hashed_coords.insert(std::pair<long long, int>(hash_coord, vert_idx)); |
||||
splat_idx[pix_idx] = vert_idx; |
||||
++vert_idx; |
||||
} |
||||
else |
||||
{ |
||||
splat_idx[pix_idx] = it->second; |
||||
} |
||||
|
||||
pref += 3; // skip 3 bytes (y u v)
|
||||
++pix_idx; |
||||
} |
||||
} |
||||
nvertices = hashed_coords.size(); |
||||
|
||||
// construct Blur matrices
|
||||
Eigen::VectorXf ones_nvertices = Eigen::VectorXf::Ones(nvertices); |
||||
Eigen::VectorXf ones_npixels = Eigen::VectorXf::Ones(npixels); |
||||
diagonal(ones_nvertices,blurs); |
||||
blurs *= 10; |
||||
for(int offset = -1; offset <= 1;++offset) |
||||
{ |
||||
if(offset == 0) continue; |
||||
for (int i = 0; i < dim; ++i) |
||||
{ |
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> blur_temp(hashed_coords.size(), hashed_coords.size()); |
||||
blur_temp.reserve(Eigen::VectorXi::Constant(nvertices,6)); |
||||
long long offset_hash_coord = offset * hash_vec[i]; |
||||
for (mapId::iterator it = hashed_coords.begin(); it != hashed_coords.end(); ++it) |
||||
{ |
||||
long long neighb_coord = it->first + offset_hash_coord; |
||||
mapId::iterator it_neighb = hashed_coords.find(neighb_coord); |
||||
if (it_neighb != hashed_coords.end()) |
||||
{ |
||||
blur_temp.insert(it->second,it_neighb->second) = 1.0f; |
||||
blur_idx.push_back(std::pair<int,int>(it->second, it_neighb->second)); |
||||
} |
||||
} |
||||
blurs += blur_temp; |
||||
} |
||||
} |
||||
blurs.finalize(); |
||||
|
||||
|
||||
//bistochastize
|
||||
int maxiter = 10; |
||||
n = ones_nvertices; |
||||
m = Eigen::VectorXf::Zero(nvertices); |
||||
for (int i = 0; i < int(splat_idx.size()); i++) |
||||
{ |
||||
m(splat_idx[i]) += 1.0f; |
||||
} |
||||
|
||||
Eigen::VectorXf bluredn(nvertices); |
||||
|
||||
for (int i = 0; i < maxiter; i++) |
||||
{ |
||||
Blur(n,bluredn); |
||||
n = ((n.array()*m.array()).array()/bluredn.array()).array().sqrt(); |
||||
} |
||||
Blur(n,bluredn); |
||||
|
||||
m = n.array() * (bluredn).array(); |
||||
diagonal(m,Dm); |
||||
diagonal(n,Dn); |
||||
} |
||||
} |
||||
|
||||
void FastBilateralSolverFilterImpl::Splat(Eigen::VectorXf& input, Eigen::VectorXf& output) |
||||
{ |
||||
output.setZero(); |
||||
for (int i = 0; i < int(splat_idx.size()); i++) |
||||
{ |
||||
output(splat_idx[i]) += input(i); |
||||
} |
||||
|
||||
} |
||||
|
||||
void FastBilateralSolverFilterImpl::Blur(Eigen::VectorXf& input, Eigen::VectorXf& output) |
||||
{ |
||||
output.setZero(); |
||||
output = input * 10; |
||||
for (int i = 0; i < int(blur_idx.size()); i++) |
||||
{ |
||||
output(blur_idx[i].first) += input(blur_idx[i].second); |
||||
} |
||||
} |
||||
|
||||
|
||||
void FastBilateralSolverFilterImpl::Slice(Eigen::VectorXf& input, Eigen::VectorXf& output) |
||||
{ |
||||
output.setZero(); |
||||
for (int i = 0; i < int(splat_idx.size()); i++) |
||||
{ |
||||
output(i) = input(splat_idx[i]); |
||||
} |
||||
} |
||||
|
||||
|
||||
void FastBilateralSolverFilterImpl::solve(cv::Mat& target, |
||||
cv::Mat& confidence, |
||||
cv::Mat& output) |
||||
{ |
||||
|
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> M(nvertices,nvertices); |
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> A_data(nvertices,nvertices); |
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> A(nvertices,nvertices); |
||||
Eigen::VectorXf b(nvertices); |
||||
Eigen::VectorXf y(nvertices); |
||||
Eigen::VectorXf y0(nvertices); |
||||
Eigen::VectorXf y1(nvertices); |
||||
Eigen::VectorXf w_splat(nvertices); |
||||
|
||||
cv::Mat x; |
||||
cv::Mat w; |
||||
cv::Mat xw; |
||||
cv::Mat filtered_xw; |
||||
cv::Mat filtered_w; |
||||
cv::Mat filtered_disp; |
||||
float fgs_colorSigma = 1.5; |
||||
float fgs_spatialSigma = 2000; |
||||
target.convertTo(x, CV_32FC1, 1.0f/255.0f); |
||||
confidence.convertTo(w, CV_32FC1); |
||||
xw = x.mul(w); |
||||
cv::ximgproc::fastGlobalSmootherFilter(guide, xw, filtered_xw, fgs_spatialSigma, fgs_colorSigma); |
||||
cv::ximgproc::fastGlobalSmootherFilter(guide, w, filtered_w, fgs_spatialSigma, fgs_colorSigma); |
||||
cv::divide(filtered_xw, filtered_w, filtered_disp, 1.0f, CV_32FC1); |
||||
|
||||
|
||||
//construct A
|
||||
w_splat.setZero(); |
||||
const float *pfw = reinterpret_cast<const float*>(w.data); |
||||
for (int i = 0; i < int(splat_idx.size()); i++) |
||||
{ |
||||
w_splat(splat_idx[i]) += pfw[i]/255.0f; |
||||
} |
||||
diagonal(w_splat,A_data); |
||||
A = bs_param.lam * (Dm - Dn * (blurs*Dn)) + A_data ; |
||||
|
||||
//construct b
|
||||
b.setZero(); |
||||
const float *pfx = reinterpret_cast<const float*>(filtered_disp.data); |
||||
for (int i = 0; i < int(splat_idx.size()); i++) |
||||
{ |
||||
b(splat_idx[i]) += pfx[i]*pfw[i]/255.0f; |
||||
} |
||||
|
||||
//construct guess for y
|
||||
y0.setZero(); |
||||
for (int i = 0; i < int(splat_idx.size()); i++) |
||||
{ |
||||
y0(splat_idx[i]) += pfx[i]; |
||||
} |
||||
y1.setZero(); |
||||
for (int i = 0; i < int(splat_idx.size()); i++) |
||||
{ |
||||
y1(splat_idx[i]) += 1.0f; |
||||
} |
||||
for (int i = 0; i < nvertices; i++) |
||||
{ |
||||
y0(i) = y0(i)/y1(i); |
||||
} |
||||
|
||||
|
||||
// solve Ay = b
|
||||
Eigen::ConjugateGradient<Eigen::SparseMatrix<float>, Eigen::Lower|Eigen::Upper> cg; |
||||
cg.compute(A); |
||||
cg.setMaxIterations(bs_param.cg_maxiter); |
||||
cg.setTolerance(bs_param.cg_tol); |
||||
// y = cg.solve(b);
|
||||
y = cg.solveWithGuess(b,y0); |
||||
std::cout << "#iterations: " << cg.iterations() << std::endl; |
||||
std::cout << "estimated error: " << cg.error() << std::endl; |
||||
|
||||
//slice
|
||||
uchar *pftar = (uchar*)(output.data); |
||||
for (int i = 0; i < int(splat_idx.size()); i++) |
||||
{ |
||||
pftar[i] = uchar(y(splat_idx[i]) * 255.0f); |
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
Ptr<FastBilateralSolverFilter> createFastBilateralSolverFilter(InputArray guide, double sigma_spatial, double sigma_luma, double sigma_chroma, int num_iter, double max_tol) |
||||
{ |
||||
return Ptr<FastBilateralSolverFilter>(FastBilateralSolverFilterImpl::create(guide, sigma_spatial, sigma_luma, sigma_chroma, num_iter, max_tol)); |
||||
} |
||||
|
||||
void fastBilateralSolverFilter(InputArray guide, InputArray src, InputArray confidence, OutputArray dst, double sigma_spatial, double sigma_luma, double sigma_chroma, int num_iter, double max_tol) |
||||
{ |
||||
Ptr<FastBilateralSolverFilter> fbs = createFastBilateralSolverFilter(guide, sigma_spatial, sigma_luma, sigma_chroma, num_iter, max_tol); |
||||
fbs->filter(src, confidence, dst); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
#else |
||||
|
||||
namespace cv |
||||
{ |
||||
namespace ximgproc |
||||
{ |
||||
|
||||
Ptr<FastBilateralSolverFilter> createFastBilateralSolverFilter(InputArray, double, double, double, int, double) |
||||
{ |
||||
CV_Error(Error::StsNotImplemented, "createFastBilateralSolverFilter : needs to be compiled with EIGEN"); |
||||
} |
||||
|
||||
void fastBilateralSolverFilter(InputArray, InputArray, InputArray, OutputArray, double, double, double, int, double) |
||||
{ |
||||
CV_Error(Error::StsNotImplemented, "fastBilateralSolverFilter : needs to be compiled with EIGEN"); |
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
#endif // HAVE_EIGEN
|
@ -0,0 +1,114 @@ |
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license. |
||||
* If you do not agree to this license, do not download, install, |
||||
* copy or use the software. |
||||
* |
||||
* |
||||
* License Agreement |
||||
* For Open Source Computer Vision Library |
||||
* (3 - clause BSD License) |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without modification, |
||||
* are permitted provided that the following conditions are met : |
||||
* |
||||
* * Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright notice, |
||||
* this list of conditions and the following disclaimer in the documentation |
||||
* and / or other materials provided with the distribution. |
||||
* |
||||
* * Neither the names of the copyright holders nor the names of the contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* This software is provided by the copyright holders and contributors "as is" and |
||||
* any express or implied warranties, including, but not limited to, the implied |
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed. |
||||
* In no event shall copyright holders or contributors be liable for any direct, |
||||
* indirect, incidental, special, exemplary, or consequential damages |
||||
* (including, but not limited to, procurement of substitute goods or services; |
||||
* loss of use, data, or profits; or business interruption) however caused |
||||
* and on any theory of liability, whether in contract, strict liability, |
||||
* or tort(including negligence or otherwise) arising in any way out of |
||||
* the use of this software, even if advised of the possibility of such damage. |
||||
*/ |
||||
|
||||
#include "test_precomp.hpp" |
||||
|
||||
#ifdef HAVE_EIGEN |
||||
|
||||
namespace opencv_test { namespace { |
||||
|
||||
using namespace std; |
||||
using namespace cv; |
||||
using namespace cv::ximgproc; |
||||
|
||||
static string getDataDir() |
||||
{ |
||||
return cvtest::TS::ptr()->get_data_path(); |
||||
} |
||||
|
||||
CV_ENUM(SrcTypes, CV_8UC1, CV_8UC3, CV_8UC4, CV_16SC1, CV_16SC3, CV_32FC1); |
||||
CV_ENUM(GuideTypes, CV_8UC1, CV_8UC3) |
||||
typedef tuple<Size, SrcTypes, GuideTypes> FBSParams; |
||||
typedef TestWithParam<FBSParams> FastBilateralSolverTest; |
||||
|
||||
TEST(FastBilateralSolverTest, SplatSurfaceAccuracy) |
||||
{ |
||||
RNG rnd(0); |
||||
int chanLut[] = {1,3,4}; |
||||
|
||||
for (int i = 0; i < 5; i++) |
||||
{ |
||||
Size sz(rnd.uniform(512, 1024), rnd.uniform(512, 1024)); |
||||
|
||||
int guideCn = rnd.uniform(0, 2); // 1 or 3 channels
|
||||
Mat guide(sz, CV_MAKE_TYPE(CV_8U, chanLut[guideCn])); |
||||
randu(guide, 0, 255); |
||||
|
||||
Scalar surfaceValue; |
||||
int srcCn = rnd.uniform(0, 3); // 1, 3 or 4 channels
|
||||
rnd.fill(surfaceValue, RNG::UNIFORM, 0, 255); |
||||
Mat src(sz, CV_MAKE_TYPE(CV_16S, chanLut[srcCn]), surfaceValue); |
||||
Mat confidence(sz, CV_MAKE_TYPE(CV_8U, 1), 255); |
||||
|
||||
double sigma_spatial = rnd.uniform(4.0, 40.0); |
||||
double sigma_luma = rnd.uniform(4.0, 40.0); |
||||
double sigma_chroma = rnd.uniform(4.0, 40.0); |
||||
|
||||
Mat res; |
||||
fastBilateralSolverFilter(guide, src, confidence, res, sigma_spatial, sigma_luma, sigma_chroma); |
||||
|
||||
// When filtering a constant image we should get the same image:
|
||||
double normL1 = cvtest::norm(src, res, NORM_L1)/src.total()/src.channels(); |
||||
EXPECT_LE(normL1, 1.0); |
||||
} |
||||
} |
||||
|
||||
TEST(FastBilateralSolverTest, ReferenceAccuracy) |
||||
{ |
||||
string dir = getDataDir() + "cv/edgefilter"; |
||||
|
||||
Mat src = imread(dir + "/kodim23.png"); |
||||
Mat ref = imread(dir + "/fgs/kodim23_lambda=1000_sigma=10.png"); |
||||
Mat confidence(src.size(), CV_MAKE_TYPE(CV_8U, 1), 255); |
||||
|
||||
ASSERT_FALSE(src.empty()); |
||||
ASSERT_FALSE(ref.empty()); |
||||
|
||||
Mat res; |
||||
fastBilateralSolverFilter(src,src,confidence,res, 16.0, 16.0, 16.0); |
||||
|
||||
double totalMaxError = 1.0/64.0*src.total()*src.channels(); |
||||
|
||||
EXPECT_LE(cvtest::norm(res, ref, NORM_L2), totalMaxError); |
||||
EXPECT_LE(cvtest::norm(res, ref, NORM_INF), 100); |
||||
} |
||||
|
||||
INSTANTIATE_TEST_CASE_P(FullSet, FastBilateralSolverTest,Combine(Values(szODD, szQVGA), SrcTypes::all(), GuideTypes::all())); |
||||
|
||||
} |
||||
} |
||||
|
||||
#endif //HAVE_EIGEN
|
Loading…
Reference in new issue