diff --git a/lib/libstereo/include/stereo.hpp b/lib/libstereo/include/stereo.hpp index 303d81509aefc7b6cb033f8fda4414315fa06f47..e5680c0f997146902c58aaebd8a434b15a820ef6 100644 --- a/lib/libstereo/include/stereo.hpp +++ b/lib/libstereo/include/stereo.hpp @@ -3,10 +3,10 @@ #include <opencv2/core/mat.hpp> #include <stereo_types.hpp> -class StereoGTCensusSgm { +class StereoGCensusSgm { public: - StereoGTCensusSgm(); - ~StereoGTCensusSgm(); + StereoGCensusSgm(); + ~StereoGCensusSgm(); void compute(cv::InputArray l, cv::InputArray r, cv::OutputArray disparity); void setPrior(cv::InputArray disp) {}; @@ -15,9 +15,9 @@ public: struct Parameters { int d_min = 0; int d_max = 0; - unsigned short P1 = 5; - unsigned short P2 = 25; - float uniqueness = std::numeric_limits<unsigned short>::max(); + float P1 = 0.0645; + float P2 = 1.2903; + float uniqueness = std::numeric_limits<float>::max(); int subpixel = 1; // subpixel interpolation method bool lr_consistency = true; int paths = AggregationDirections::HORIZONTAL | @@ -27,9 +27,31 @@ public: }; Parameters params; + enum Pattern { + DENSE, + SPARSE, + RANDOM, + GCT, + }; + + /** + * Set pattern. + * + * DENSE: size required, param ignored + * SPARSE: size and parama required, param is step (number of skipped pixels) + * RANDOM: size and param required, param is number of edges + * GCT: param required, size ignored, param is pattern type (number of edges), see the paper for description + */ + void setPattern(Pattern type, cv::Size size, int param=-1); + /** + * Set custom pattern. + */ + void setPattern(const std::vector<std::pair<cv::Point2i, cv::Point2i>> &edges); + private: struct Impl; Impl *impl_; + std::vector<std::pair<cv::Point2i, cv::Point2i>> pattern_; }; diff --git a/lib/libstereo/middlebury/algorithms.hpp b/lib/libstereo/middlebury/algorithms.hpp index f3878fe0ce8e14a6ab9d62a2ef6ef26ffda8382d..0b510df4a46349397b8b738884f570e7c1740df6 100644 --- a/lib/libstereo/middlebury/algorithms.hpp +++ b/lib/libstereo/middlebury/algorithms.hpp @@ -337,23 +337,26 @@ namespace Impl { } }; + /** Generalized Census Transform */ struct GCTSgm : public Algorithm { - GCTSgm() { P1 = 12.0f; P2 = 52.0f; } // Tuned to total error 2.0 + + GCTSgm() { P1 = 30.0f / float(9*7-1); P2 = 132.0f / float(9*7-1); } virtual void run(const MiddleburyData &data, cv::Mat &disparity) override { - StereoGTCensusSgm stereo; + StereoGCensusSgm stereo; + stereo.setPattern(StereoGCensusSgm::Pattern::GCT, {0, 0}, 12); + stereo.params.P1 = P1; stereo.params.P2 = P2; stereo.params.subpixel = subpixel; stereo.params.lr_consistency = lr_consistency; - stereo.params.debug = false; + stereo.params.debug = true; stereo.params.d_min = data.calib.vmin; stereo.params.d_max = data.calib.vmax; stereo.compute(data.imL, data.imR, disparity); } }; - } static const std::map<std::string, Algorithm*> algorithms = { diff --git a/lib/libstereo/src/algorithms/gct.cu b/lib/libstereo/src/algorithms/gct.cu index ebe8d906cbcd555ca561f3fa5ddeb9719d3917df..cd070d1e4e15ac7d7abe4ac2df0af7ce26114bf6 100644 --- a/lib/libstereo/src/algorithms/gct.cu +++ b/lib/libstereo/src/algorithms/gct.cu @@ -2,64 +2,78 @@ #include "stereosgm.hpp" #include "../costs/gct.hpp" -struct StereoGTCensusSgm::Impl : public StereoSgm<GeneralizedCensusMatchingCost, StereoGTCensusSgm::Parameters> { +struct StereoGCensusSgm::Impl : public StereoSgm<GeneralizedCensusMatchingCost, StereoGCensusSgm::Parameters> { Array2D<uchar> l; Array2D<uchar> r; - Impl(StereoGTCensusSgm::Parameters ¶ms, int width, int height, int dmin, int dmax) : + Impl(StereoGCensusSgm::Parameters ¶ms, int width, int height, int dmin, int dmax) : StereoSgm(params, width, height, dmin, dmax), l(width, height), r(width, height) {} }; -StereoGTCensusSgm::StereoGTCensusSgm() : impl_(nullptr) { +StereoGCensusSgm::StereoGCensusSgm() : impl_(nullptr) { impl_ = new Impl(params, 0, 0, 0, 0); } -void StereoGTCensusSgm::compute(cv::InputArray l, cv::InputArray r, cv::OutputArray disparity) { - - //cudaSetDevice(0); +void StereoGCensusSgm::compute(cv::InputArray l, cv::InputArray r, cv::OutputArray disparity) { if (l.rows() != impl_->cost.height() || r.cols() != impl_->cost.width()) { delete impl_; impl_ = nullptr; impl_ = new Impl(params, l.cols(), l.rows(), params.d_min, params.d_max); + impl_->cost.setEdges(pattern_); } mat2gray(l, impl_->l); mat2gray(r, impl_->r); - - impl_->cost.setEdges({ - {{-1,-1}, {1,1}}, - {{1,-1}, {-1,1}}, - {{0,-1}, {0,1}}, - {{-1,0}, {1,0}}, - {{-2,-2}, {2,2}}, - {{2,-2}, {-2,2}}, - {{0,-2}, {0,2}}, - {{-2,0}, {2,0}} - }); - /* wx * wy square window - std::vector<std::pair<std::pair<int, int>,std::pair<int, int>>> edges; - { - int wx = 7; - int wy = 7; - - for (int x = -wx/2; x <= wx/2; x++) { - for (int y = -wy/2; y <= wy/2; y++) { - edges.push_back({{0,0},{y,x}}); - } - } - }*/ - impl_->cost.set(impl_->l, impl_->r); cudaSafeCall(cudaDeviceSynchronize()); impl_->compute(disparity); - /*WinnerTakesAll<GeneralizedCensusMatchingCost> wta; + + median_filter(impl_->wta.disparity, disparity); + + /* without sgm: + mat2gray(l, impl_->l); + mat2gray(r, impl_->r); + impl_->cost.set(impl_->l, impl_->r); + + WinnerTakesAll<GeneralizedCensusMatchingCost> wta; wta(impl_->cost, 0, true); - median_filter(wta.disparity, disparity);*/ + median_filter(wta.disparity, disparity); + */ +} + +void StereoGCensusSgm::setPattern(StereoGCensusSgm::Pattern pattern, cv::Size size, int param) { + switch(pattern) { + case Pattern::DENSE: + pattern_ = pattern_dense(size); + break; + + case Pattern::SPARSE: + pattern_ = pattern_sparse(size, param); + break; + + case Pattern::RANDOM: + pattern_ = pattern_random(size, param); + break; + + case Pattern::GCT: + pattern_ = pattern_gct(param); + break; + + default: + printf("invalid pattern\n"); + throw std::exception(); + } + impl_->cost.setEdges(pattern_); +} + +void StereoGCensusSgm::setPattern(const std::vector<std::pair<cv::Point2i, cv::Point2i>> &edges) { + pattern_ = edges; + impl_->cost.setEdges(edges); } -StereoGTCensusSgm::~StereoGTCensusSgm() { +StereoGCensusSgm::~StereoGCensusSgm() { if (impl_) { delete impl_; impl_ = nullptr; diff --git a/lib/libstereo/src/costs/census.cu b/lib/libstereo/src/costs/census.cu index bd3e01357053a01c0fe3fd984e178f8c445b2837..124eda70375fb6ebdf2d6d4d7d79643d8718c95d 100644 --- a/lib/libstereo/src/costs/census.cu +++ b/lib/libstereo/src/costs/census.cu @@ -27,6 +27,10 @@ namespace algorithms { const int y_ = y + wy; const int x_ = x + wx; + if (y == 0 && x == 0) { + continue; + } + // zero if first value, otherwise shift to left res = (res << 1); res |= (center < (im(y_,x_)) ? 1 : 0); diff --git a/lib/libstereo/src/costs/gct.cu b/lib/libstereo/src/costs/gct.cu index 5d76340b969d16e4c0a37ce6d44820dc2ac7caae..f2b1a5d099c600d17815fffaf83fe8e60e211f00 100644 --- a/lib/libstereo/src/costs/gct.cu +++ b/lib/libstereo/src/costs/gct.cu @@ -1,114 +1,131 @@ #include "gct.hpp" #include "../util.hpp" -static const int WX = 11; -static const int WY = 11; +#include <random> + +static const int NBITS = 128; namespace algorithms { /** Fife, W. S., & Archibald, J. K. (2012). Improved census transforms for * resource-optimized stereo vision. IEEE Transactions on Circuits and * Systems for Video Technology, 23(1), 60-73. */ - template<int WINX, int WINY> + + template<int BITS> struct GeneralizedCensusTransform { - __host__ __device__ inline void window(const int y, const int x, uint64_t* __restrict__ out) { + static_assert(BITS%64 == 0); + + __host__ __device__ inline void compute(const int y, const int x, uint64_t* __restrict__ out) { uint8_t i = 0; // bit counter for *out + // BUG in operator(), gets called more than once per pixel; local + // variable for sub-bitstring to avoid data race (no read + // dependency to out; writes are identical) + uint64_t res = 0; for (int e = 0; e < nedges; e++) { // edges contain window indices, calculate window coordinates - const int y1 = y + edges(e,0) % WINY - WINY/2; - const int x1 = x + edges(e,0) / WINY - WINX/2; + //const int y1 = y + edges(e,0) % WINY - WINY/2; + //const int x1 = x + edges(e,0) / WINY - WINX/2; + //const int y2 = y + edges(e,1) % WINY - WINY/2; + //const int x2 = x + edges(e,1) / WINY - WINX/2; - const int y2 = y + edges(e,1) % WINY - WINY/2; - const int x2 = x + edges(e,1) / WINY - WINX/2; + // edges contain relative pixel coordinates + const int x1 = x + edges(e,0); + const int y1 = y + edges(e,1); + const int x2 = x + edges(e,2); + const int y2 = y + edges(e,3); - // zero if first value, otherwise shift to left - if (i % 64 == 0) { *out = 0; } - else { *out = (*out << 1); } - *out |= (im(y1,x1) < (im(y2,x2)) ? 1 : 0); + res = (res << 1); + res |= ((im(y1,x1) < im(y2,x2)) ? 1 : 0); - i += 1; // if all bits set, continue to next element - if (i % 64 == 0) { out++; } + if (++i % 64 == 0) { + *out = res; + out++; + } + } + + // zero remaining bits (less edges than bits in output array) + for(i = BITS/64 - i/64; i > 0; i--) { + *out++ = res; + res = 0; } } __host__ __device__ void operator()(ushort2 thread, ushort2 stride, ushort2 size) { - for (int y = thread.y+WINY/2; y<size.y-WINY/2-1; y+=stride.y) { - for (int x = thread.x+WINX/2; x<size.x-WINX/2-1; x+=stride.x) { - window(y, x, &(out(y, x*WSTEP))); + for (int y = thread.y+winy/2; y<size.y-winy/2-1; y+=stride.y) { + for (int x = thread.x+winx/2; x<size.x-winx/2-1; x+=stride.x) { + compute(y, x, &(out(y, x*WSTEP))); } } } int nedges; - Array2D<uchar>::Data edges; + int winx; + int winy; + Array2D<char>::Data edges; Array2D<uchar>::Data im; Array2D<uint64_t>::Data out; // number of uint64_t values for each window - static constexpr int WSTEP = (WINX*WINY - 1)/(sizeof(uint64_t)*8) + 1; + static constexpr int WSTEP = (BITS - 1)/(sizeof(uint64_t)*8) + 1; }; } void GeneralizedCensusMatchingCost::set(const Array2D<uchar> &l, const Array2D<uchar> &r) { + if (edges_.height == 0) { + printf("edges must be set before processing input images\n"); + throw std::exception(); + } - parallel2D<algorithms::GeneralizedCensusTransform<WX,WY>>({edges_.height, edges_.data(), l.data(), ct_l_.data()}, l.width, l.height); - parallel2D<algorithms::GeneralizedCensusTransform<WX,WY>>({edges_.height, edges_.data(), r.data(), ct_r_.data()}, r.width, r.height); + int winx = std::max(std::abs(pmin.x), pmax.x)*2 + 1; + int winy = std::max(std::abs(pmin.y), pmax.y)*2 + 1; + parallel2D<algorithms::GeneralizedCensusTransform<128>>({ + edges_.height, winx, winy, + edges_.data(), l.data(), ct_l_.data() + }, l.width, l.height); + parallel2D<algorithms::GeneralizedCensusTransform<128>>({ + edges_.height, winx, winy, + edges_.data(), r.data(), ct_r_.data() + }, r.width, r.height); } -void GeneralizedCensusMatchingCost::setEdges(const std::vector<std::pair<int, int>> &edges) { - if (edges.size() >= (WX * WY)) { +void GeneralizedCensusMatchingCost::setEdges(const std::vector<std::pair<cv::Point2i, cv::Point2i>> &edges) { + if (edges.size() > NBITS) { + printf("Too many edges %i, maximum number %i\n", int(edges.size()), NBITS); throw std::exception(); // too many edges } - cv::Mat data(cv::Size(2, edges.size()), CV_8UC1); + cv::Mat data_(cv::Size(4, edges.size()), CV_8SC1); for (size_t i = 0; i < edges.size(); i++) { - if (edges[i].first < 0 || edges[0].second < 0) { - throw std::exception(); // indices must be positive - } - - data.at<uchar>(i,0) = edges[i].first; - data.at<uchar>(i,1) = edges[i].second; + const auto &p1 = edges[i].first; + const auto &p2 = edges[i].second; + + data_.at<char>(i,0) = p1.x; + data_.at<char>(i,1) = p1.y; + data_.at<char>(i,2) = p2.x; + data_.at<char>(i,3) = p2.y; + + pmax.x = std::max(pmax.x, std::max(p1.x, p2.x)); + pmax.y = std::max(pmax.y, std::max(p1.y, p2.y)); + pmin.x = std::min(pmax.x, std::min(p1.x, p2.x)); + pmin.y = std::min(pmax.y, std::min(p1.y, p2.y)); } - edges_.create(2, edges.size()); + edges_.create(4, edges.size()); #ifdef USE_GPU - edges_.toGpuMat().upload(data); + edges_.toGpuMat().upload(data_); #else - data.copyTo(edges_.toMat()); + data_.copyTo(edges_.toMat()); #endif -} - -void GeneralizedCensusMatchingCost::setEdges(const std::vector<std::pair<std::pair<int, int>,std::pair<int, int>>> &edges) { - std::vector<std::pair<int, int>> edges_idx; - for (const auto& p : edges) { - const auto& p1 = p.first; - const auto& p2 = p.second; - - int i1 = p1.first * WX + p1.second + (WX*WY-1)/2; - int i2 = p2.first * WX + p2.second + (WX*WY-1)/2; - printf("i1: %i, i2: %i\n", i1, i2); - edges_idx.push_back({i1, i2}); - } - /* TODO: move to unit test - for (int i = 0; i < edges.size(); i++) { - auto p = edges_idx[i]; - - const int y1 = p.first % WY - WY/2; - const int x1 = p.first / WY - WX/2; - - const int y2 = p.second % WY - WY/2; - const int x2 = p.second / WY - WX/2; - printf("(%i,%i), (%i,%i)\n", y1, x1, y2, x2); - }*/ - - setEdges(edges_idx); + // normalization factor: 1.0/(number of comparisons) + data().normalize = 1.0f/float(edges.size()); } + void GeneralizedCensusMatchingCost::set(cv::InputArray l, cv::InputArray r) { if (l.type() != CV_8UC1 || r.type() != CV_8UC1) { throw std::exception(); } if (l.rows() != r.rows() || l.cols() != r.cols() || l.rows() != height() || l.cols() != width()) { @@ -126,46 +143,102 @@ void GeneralizedCensusMatchingCost::set(cv::InputArray l, cv::InputArray r) { set(Array2D<uchar>(ml), Array2D<uchar>(mr)); } else { + printf("Bad input array type\n"); throw std::exception(); } } -//////////////////////////////////////////////////////////////////////////////// -/* -struct StereoCensusSgm::Impl : public StereoSgm<CensusMatchingCost, StereoCensusSgm::Parameters> { - Array2D<uchar> l; - Array2D<uchar> r; +// ==== Pattern generators ===================================================== + +std::vector<std::pair<cv::Point2i, cv::Point2i>> pattern_dense(const cv::Size size) { + return pattern_sparse(size, 1); +} + +std::vector<std::pair<cv::Point2i, cv::Point2i>> pattern_sparse(const cv::Size size, int step) { + std::vector<std::pair<cv::Point2i, cv::Point2i>> pattern; - Impl(StereoCensusSgm::Parameters ¶ms, int width, int height, int dmin, int dmax) : - StereoSgm(params, width, height, dmin, dmax), l(width, height), r(width, height) {} -}; + for (int y = -size.height/2; y <= size.height/2; y += step) { + for (int x = -size.width/2; x <= size.width/2; x += step) { + if (cv::Point2i{x, y} == cv::Point2i{0, 0}) { continue; } + pattern.push_back({{0, 0}, {x, y}}); + } + } -StereoCensusSgm::StereoCensusSgm() : impl_(nullptr) { - impl_ = new Impl(params, 0, 0, 0, 0); + return pattern; } -void StereoCensusSgm::compute(cv::InputArray l, cv::InputArray r, cv::OutputArray disparity) { +std::vector<std::pair<cv::Point2i, cv::Point2i>> pattern_random(const cv::Size size, int nedges) { + std::vector<std::pair<cv::Point2i, cv::Point2i>> pattern; - //cudaSetDevice(0); + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> rand_x(-size.width/2, size.width/2); + std::uniform_int_distribution<> rand_y(-size.height/2, size.height/2); + + for (int i = 0; i < nedges; i++) { + cv::Point2i p1; + cv::Point2i p2; + do { + p1 = {rand_x(gen), rand_y(gen)}; + p2 = {rand_x(gen), rand_y(gen)}; + } + while (p1 == p2); // try again if points happen to be same - if (l.rows() != impl_->cost.height() || r.cols() != impl_->cost.width()) { - delete impl_; impl_ = nullptr; - impl_ = new Impl(params, l.cols(), l.rows(), params.d_min, params.d_max); + pattern.push_back({p1, p2}); } - mat2gray(l, impl_->l); - mat2gray(r, impl_->r); - impl_->cost.setPattern(params.pattern); - impl_->cost.set(impl_->l, impl_->r); + return pattern; +} - cudaSafeCall(cudaDeviceSynchronize()); - impl_->compute(disparity); +std::vector<std::pair<cv::Point2i, cv::Point2i>> pattern_random(const cv::Size size) { + return pattern_random(size, size.width*size.height); } -StereoCensusSgm::~StereoCensusSgm() { - if (impl_) { - delete impl_; - impl_ = nullptr; +std::vector<std::pair<cv::Point2i, cv::Point2i>> pattern_gct(int nedges) { + std::vector<std::pair<cv::Point2i, cv::Point2i>> pattern; + pattern.reserve(nedges); + switch(nedges) { + case 16: + pattern.push_back({{-2, -1}, {2, 1}}); + pattern.push_back({{-2, 1}, {2, -1}}); + pattern.push_back({{-1, -2}, {1, 2}}); + pattern.push_back({{1, -2}, {-1, -2}}); + [[fallthrough]] + + case 12: + pattern.push_back({{-1, -1}, {1, 0}}); + pattern.push_back({{1, -1}, {-1, 0}}); + pattern.push_back({{-1, 1}, {1, 0}}); + pattern.push_back({{1, 1}, {-1, 0}}); + [[fallthrough]] + + case 8: + pattern.push_back({{-2, -2}, {2, 2}}); + pattern.push_back({{-2, 2}, {2, -2}}); + pattern.push_back({{0, -2}, {0, 2}}); + pattern.push_back({{-2, 0}, {2, 0}}); + [[fallthrough]] + + case 4: + pattern.push_back({{-1, -1}, {1, 1}}); + pattern.push_back({{-1, 1}, {1, -1}}); + [[fallthrough]] + + case 2: + pattern.push_back({{0, -1}, {0, 1}}); + [[fallthrough]] + + case 1: + pattern.push_back({{-1, 0}, {1, 0}}); + break; + + default: + printf("Bad number of edges %i, valid values are 1, 2, 4, 8 and 16", nedges); + throw std::exception(); + } + if (nedges != pattern.size()) { + printf("error (assert): pattern size incorrect"); + throw std::exception(); } + return pattern; } -*/ diff --git a/lib/libstereo/src/costs/gct.hpp b/lib/libstereo/src/costs/gct.hpp index 90f7d855556e4cb386fcf9fd0eb3b23af89c4f64..4214df43f9a9a1d534dfe8e626f25cfaae5760c2 100644 --- a/lib/libstereo/src/costs/gct.hpp +++ b/lib/libstereo/src/costs/gct.hpp @@ -1,18 +1,23 @@ #pragma once +#include <opencv2/core.hpp> #include "../dsbase.hpp" #include "../array2d.hpp" #include "census.hpp" +/** + * Generalized Census Transform + */ + namespace impl { - template<uint8_t WMAX> - using GeneralizedCensusMatchingCost = HammingCost<WMAX*WMAX>; + template<uint8_t BITS> + using GeneralizedCensusMatchingCost = NormalizedHammingCost<BITS>; } -class GeneralizedCensusMatchingCost : public DSBase<impl::GeneralizedCensusMatchingCost<11>> { +class GeneralizedCensusMatchingCost : public DSBase<impl::GeneralizedCensusMatchingCost<128>> { public: - typedef impl::GeneralizedCensusMatchingCost<11> DataType; - typedef unsigned short Type; + typedef impl::GeneralizedCensusMatchingCost<128> DataType; + typedef float Type; GeneralizedCensusMatchingCost() : DSBase<DataType>(0, 0, 0, 0) {}; GeneralizedCensusMatchingCost(int width, int height, int disp_min, int disp_max) @@ -23,10 +28,12 @@ public: data().r = ct_r_.data(); } - // pairs of indices (window size 11x11, origin top left) - void setEdges(const std::vector<std::pair<int, int>> &edges); - // pairs of (y,x) coordinates (relative to window, origin at center) - void setEdges(const std::vector<std::pair<std::pair<int, int>,std::pair<int, int>>> &edges); + /** Pairs of (x, y) coordinates (relative to window, origin at center) + * indices must fit in signed char [-128,127]. + * TODO: Indices must fit within 11x11 window (operator() in + * GeneralizedCensusTransform) + */ + void setEdges(const std::vector<std::pair<cv::Point2i,cv::Point2i>> &edges); void set(cv::InputArray l, cv::InputArray r); void set(const Array2D<uchar>& l, const Array2D<uchar>& r); @@ -35,5 +42,20 @@ public: protected: Array2D<uint64_t> ct_l_; Array2D<uint64_t> ct_r_; - Array2D<uchar> edges_; + Array2D<char> edges_; + + cv::Point2i pmax; + cv::Point2i pmin; }; + +// ==== Pattern generators ===================================================== + +std::vector<std::pair<cv::Point2i, cv::Point2i>> pattern_dense(const cv::Size size); + +std::vector<std::pair<cv::Point2i, cv::Point2i>> pattern_sparse(const cv::Size size, int step=2); + +std::vector<std::pair<cv::Point2i, cv::Point2i>> pattern_random(const cv::Size size, int nedges); +std::vector<std::pair<cv::Point2i, cv::Point2i>> pattern_random(const cv::Size size); + +/** patterns presented in the original paper */ +std::vector<std::pair<cv::Point2i, cv::Point2i>> pattern_gct(int nedges); diff --git a/lib/libstereo/src/util_opencv.hpp b/lib/libstereo/src/util_opencv.hpp index 94e9db6fe735ddcc48661ed42ceaac95f96be94a..fc30984956fff26a27386d7426866ead584f3314 100644 --- a/lib/libstereo/src/util_opencv.hpp +++ b/lib/libstereo/src/util_opencv.hpp @@ -10,8 +10,14 @@ #include <opencv2/cudaimgproc.hpp> static void mat2gray(const cv::cuda::GpuMat &in, Array2D<unsigned char> &out) { - if (in.depth() != CV_8U) { throw std::exception(); } - if (out.width != in.cols || out.height != in.rows) { throw std::exception(); } + if (in.depth() != CV_8U) { + printf("input must be 8-bit\n"); + throw std::exception(); + } + if (out.width != in.cols || out.height != in.rows) { + printf("input and output have different sizes\n"); + throw std::exception(); + } switch (in.channels()) { case 4: @@ -27,14 +33,21 @@ static void mat2gray(const cv::cuda::GpuMat &in, Array2D<unsigned char> &out) { break; default: + printf("bad number of channels\n"); throw std::exception(); } } #endif static void mat2gray(const cv::Mat &in, Array2D<unsigned char> &out) { - if (in.depth() != CV_8U) { throw std::exception(); } - if (out.width != in.cols || out.height != in.rows) { throw std::exception(); } + if (in.depth() != CV_8U) { + printf("input must be 8-bit\n"); + throw std::exception(); + } + if (out.width != in.cols || out.height != in.rows) { + printf("input and output have different sizes\n"); + throw std::exception(); + } #ifndef USE_GPU switch (in.channels()) { @@ -51,6 +64,7 @@ static void mat2gray(const cv::Mat &in, Array2D<unsigned char> &out) { break; default: + printf("bad number of channels\n"); throw std::exception(); } #else @@ -69,6 +83,7 @@ static void mat2gray(const cv::Mat &in, Array2D<unsigned char> &out) { break; default: + printf("bad number of channels\n"); throw std::exception(); } @@ -86,6 +101,7 @@ static void mat2gray(cv::InputArray in, Array2D<unsigned char> &out) { mat2gray(in.getMat(), out); } else { + printf("bad input type\n"); throw std::exception(); } }