rcv
Rand's OpenCV Utilities
rcv.hpp
00001 #ifndef RCV_HPP
00002 #define RCV_HPP
00003 
00004 #include <time.h>
00005 #include <opencv2/core/core.hpp>
00006 #include <opencv2/imgproc/imgproc.hpp>
00007 #include <opencv2/highgui/highgui.hpp>
00008 #include <type_traits>
00009 #include <typeindex>
00010 #include <stdexcept>
00011 #include <iomanip>
00012 #include <sstream>
00013 
00014 #include <iostream>
00015 #include <numeric>
00016 
00017 namespace rcv
00018 {
00019 
00023 
00024 
00025   template<class T> struct type2cv {};
00026   template<> struct type2cv<uint8_t>  { static const int value = CV_8U;  };
00027   template<> struct type2cv<int8_t>   { static const int value = CV_8S;  };
00028   template<> struct type2cv<uint16_t> { static const int value = CV_16U; };
00029   template<> struct type2cv<int16_t>  { static const int value = CV_16S; };
00030   template<> struct type2cv<int32_t>  { static const int value = CV_32S; };
00031   template<> struct type2cv<float>    { static const int value = CV_32F; };
00032   template<> struct type2cv<double>   { static const int value = CV_64F; };
00033 
00035 
00041   std::string type2string(int imgTypeInt)
00042   {
00043     int numImgTypes = 35; // 7 base types, with five channel options each (none or C1, ..., C4)
00044 
00045     int enum_ints[] = {
00046       CV_8U,  CV_8UC1,  CV_8UC2,  CV_8UC3,  CV_8UC4,
00047       CV_8S,  CV_8SC1,  CV_8SC2,  CV_8SC3,  CV_8SC4,
00048       CV_16U, CV_16UC1, CV_16UC2, CV_16UC3, CV_16UC4,
00049       CV_16S, CV_16SC1, CV_16SC2, CV_16SC3, CV_16SC4,
00050       CV_32S, CV_32SC1, CV_32SC2, CV_32SC3, CV_32SC4,
00051       CV_32F, CV_32FC1, CV_32FC2, CV_32FC3, CV_32FC4,
00052       CV_64F, CV_64FC1, CV_64FC2, CV_64FC3, CV_64FC4};
00053 
00054     static std::string const enum_strings[] = {
00055       "CV_8U",  "CV_8UC1",  "CV_8UC2",  "CV_8UC3",  "CV_8UC4",
00056       "CV_8S",  "CV_8SC1",  "CV_8SC2",  "CV_8SC3",  "CV_8SC4",
00057       "CV_16U", "CV_16UC1", "CV_16UC2", "CV_16UC3", "CV_16UC4",
00058       "CV_16S", "CV_16SC1", "CV_16SC2", "CV_16SC3", "CV_16SC4",
00059       "CV_32S", "CV_32SC1", "CV_32SC2", "CV_32SC3", "CV_32SC4",
00060       "CV_32F", "CV_32FC1", "CV_32FC2", "CV_32FC3", "CV_32FC4",
00061       "CV_64F", "CV_64FC1", "CV_64FC2", "CV_64FC3", "CV_64FC4"};
00062 
00063     for(int i=0; i<numImgTypes; i++)
00064     {
00065       if(imgTypeInt == enum_ints[i]) return enum_strings[i];
00066     }
00067     return "unknown image type";
00068   }
00069 
00071 
00076   double constexpr white_value(int mat_type)
00077   {
00078     return CV_MAT_TYPE(mat_type) == CV_8U ? 255.0 :
00079       (
00080        CV_MAT_TYPE(mat_type) == CV_16U ? 65536.0 : 
00081        (
00082         1.0
00083        )
00084       );
00085   }
00086 
00088 
00089   cv::Mat convert_and_scale(cv::Mat const & image, int const rtype)
00090   {
00091     cv::Mat result;
00092     image.convertTo(result, rtype, white_value(rtype) / white_value(image.type()));
00093     return result;
00094   }
00095 
00097 
00119 #define RCV_DISPATCH(type, function_name, ...)                                               \
00120   [&]() {                                                                                    \
00121     switch(CV_MAT_TYPE(type))                                                                \
00122     {                                                                                        \
00123       case CV_8U:  return function_name<uint8_t>(__VA_ARGS__);                               \
00124       case CV_8S:  return function_name<int8_t>(__VA_ARGS__);                                \
00125       case CV_16U: return function_name<uint16_t>(__VA_ARGS__);                              \
00126       case CV_16S: return function_name<int16_t>(__VA_ARGS__);                               \
00127       case CV_32S: return function_name<int32_t>(__VA_ARGS__);                               \
00128       case CV_32F: return function_name<float>(__VA_ARGS__);                                 \
00129       case CV_64F: return function_name<double>(__VA_ARGS__);                                \
00130       default: throw std::runtime_error("Unsupported data type: " + rcv::type2string(type)); \
00131     };                                                                                       \
00132   }()
00133   
00135 
00136 #define RCV_DISPATCH_NO_RETURN(type, function_name, ...)                                     \
00137   [&]() {                                                                                    \
00138     switch(CV_MAT_TYPE(type))                                                                \
00139     {                                                                                        \
00140       case CV_8U:  function_name<uint8_t>(__VA_ARGS__);   break;                             \
00141       case CV_8S:  function_name<int8_t>(__VA_ARGS__);    break;                             \
00142       case CV_16U: function_name<uint16_t>(__VA_ARGS__);  break;                             \
00143       case CV_16S: function_name<int16_t>(__VA_ARGS__);   break;                             \
00144       case CV_32S: function_name<int32_t>(__VA_ARGS__);   break;                             \
00145       case CV_32F: function_name<float>(__VA_ARGS__);     break;                             \
00146       case CV_64F: function_name<double>(__VA_ARGS__);    break;                             \
00147       default: throw std::runtime_error("Unsupported data type: " + rcv::type2string(type)); \
00148     };                                                                                       \
00149   }()
00150 
00164   // ######################################################################
00166 
00172   cv::Mat hcat(cv::Mat left, cv::Mat right, cv::Scalar fill = cv::Scalar(0))
00173   {
00174     if(left.type() != right.type())
00175       throw std::runtime_error("In rcv::hcat: mismatched types between "
00176           "left (" + rcv::type2string(left.type()) + ") "
00177           "and right (" + rcv::type2string(right.type()) + ")");
00178 
00179     int const rows = std::max(left.rows, right.rows);
00180     int const cols = left.cols + right.cols;
00181     cv::Mat ret(rows, cols, left.type(), fill); 
00182 
00183     cv::Mat left_ret_roi = ret(cv::Rect(0, 0, left.cols, left.rows));
00184     left.copyTo(left_ret_roi);
00185 
00186     cv::Mat right_ret_roi = ret(cv::Rect(left.cols, 0, right.cols, right.rows));
00187     right.copyTo(right_ret_roi);
00188 
00189     return ret;
00190   }
00191 
00192   // ######################################################################
00194 
00204   cv::Mat hcat(std::vector<cv::Mat> const & images, cv::Scalar fill = cv::Scalar(0))
00205   {
00206     if(images.empty()) return cv::Mat();
00207     if(images.size() == 1) return images[0];
00208 
00209     for(size_t i=1; i<images.size(); ++i)
00210       if(images[i].type() != images[0].type())
00211         throw std::runtime_error("In rcv::hcat: mismatched types between "
00212             "images[0] (" + rcv::type2string(images[0].type()) + ") "
00213             "and images[" + std::to_string(i) + "] (" + rcv::type2string(images[i].type()) + ")");
00214 
00215     int const rows = std::max_element(images.begin(), images.end(), [](cv::Mat a, cv::Mat b) { return a.rows < b.rows; })->rows;
00216     int cols = std::accumulate(images.begin(), images.end(), 0, [](int n, cv::Mat m) { return n+m.cols; });
00217 
00218     cv::Mat ret(rows, cols, images[0].type(), fill);
00219 
00220     int c = 0;
00221     for(cv::Mat const & image : images)
00222     {
00223       cv::Mat roi = ret(cv::Rect(c, 0, image.cols, image.rows));
00224       image.copyTo(roi);
00225       c += image.cols;
00226     }
00227 
00228     return ret;
00229   }
00230  
00231   // ######################################################################
00233 
00239   cv::Mat vcat(cv::Mat top, cv::Mat bottom, cv::Scalar fill = cv::Scalar(0))
00240   {
00241     if(top.type() != bottom.type())
00242       throw std::runtime_error("In rcv::vcat: mismatched types between "
00243           "top (" + rcv::type2string(top.type()) + ") "
00244           "and bottom (" + rcv::type2string(bottom.type()) + ")");
00245 
00246     int const rows = top.rows + bottom.rows;
00247     int const cols = std::max(top.cols, bottom.cols);
00248     cv::Mat ret(rows, cols, top.type(), fill); 
00249 
00250     cv::Mat top_ret_roi = ret(cv::Rect(0, 0, top.cols, top.rows));
00251     top.copyTo(top_ret_roi);
00252 
00253     cv::Mat bottom_ret_roi = ret(cv::Rect(0, top.rows, bottom.cols, bottom.rows));
00254     bottom.copyTo(bottom_ret_roi);
00255 
00256     return ret;
00257   }
00258 
00259   // ######################################################################
00261 
00271   cv::Mat vcat(std::vector<cv::Mat> const & images, cv::Scalar fill = cv::Scalar(0))
00272   {
00273     if(images.empty()) return cv::Mat();
00274     if(images.size() == 1) return images[0];
00275 
00276     for(size_t i=1; i<images.size(); ++i)
00277       if(images[i].type() != images[0].type())
00278         throw std::runtime_error("In rcv::vcat: mismatched types between "
00279             "images[0] (" + rcv::type2string(images[0].type()) + ") "
00280             "and images[" + std::to_string(i) + "] (" + rcv::type2string(images[i].type()) + ")");
00281 
00282     int rows = std::accumulate(images.begin(), images.end(), 0, [](int n, cv::Mat m) { return n+m.rows; });
00283     int const cols = std::max_element(images.begin(), images.end(), [](cv::Mat a, cv::Mat b) { return a.cols < b.cols; })->cols;
00284 
00285     cv::Mat ret(rows, cols, images[0].type(), fill);
00286 
00287     int r = 0;
00288     for(cv::Mat const & image : images)
00289     {
00290       cv::Mat roi = ret(cv::Rect(0, r, image.cols, image.rows));
00291       image.copyTo(roi);
00292       r += image.rows;
00293     }
00294 
00295 
00296     return ret;
00297   }
00304   // ######################################################################
00305   class AutoScaleIndicator { };
00307   AutoScaleIndicator autoscale;
00308 
00309   template<class Iterator, class MaxScaleValue> 
00310     auto get_max_value(Iterator const begin, Iterator const end, MaxScaleValue max_value __attribute__ ((unused))) -> 
00311     typename std::enable_if<std::is_same<MaxScaleValue, AutoScaleIndicator>::value, typename std::remove_reference<decltype(*begin)>::type>::type
00312     { return *std::max_element(begin, end); }
00313 
00314   template<class Iterator, class MaxScaleValue> 
00315     auto get_max_value(Iterator const begin  __attribute__ ((unused)), Iterator const end  __attribute__ ((unused)), MaxScaleValue max_value) -> 
00316     typename std::enable_if<!std::is_same<MaxScaleValue, AutoScaleIndicator>::value, typename std::remove_reference<decltype(*begin)>::type>::type
00317     { return max_value; }
00318 
00319   template<class Iterator, class MaxScaleValue> 
00320     auto get_min_value(Iterator const begin, Iterator const end, MaxScaleValue min_value __attribute__ ((unused))) -> 
00321     typename std::enable_if<std::is_same<MaxScaleValue, AutoScaleIndicator>::value, typename std::remove_reference<decltype(*begin)>::type>::type
00322     { return *std::min_element(begin, end); }
00323 
00324   template<class Iterator, class MaxScaleValue> 
00325     auto get_min_value(Iterator const begin  __attribute__ ((unused)), Iterator const end  __attribute__ ((unused)), MaxScaleValue min_value) -> 
00326     typename std::enable_if<!std::is_same<MaxScaleValue, AutoScaleIndicator>::value, typename std::remove_reference<decltype(*begin)>::type>::type
00327     { return min_value; }
00328 
00329   // ######################################################################
00331 
00341   template<class Iterator, class MinScaleValue=AutoScaleIndicator, class MaxScaleValue=AutoScaleIndicator>
00342   cv::Mat plot(Iterator const begin, Iterator const end, cv::Size plot_size,
00343       MinScaleValue min_value = autoscale, MaxScaleValue max_value = autoscale,
00344       cv::Scalar line_color=cv::Scalar::all(255), int line_width=1, int image_type=CV_8UC3, bool write_limits = false)
00345   {
00346     typedef typename std::remove_reference<decltype(*begin)>::type T;
00347 
00348     cv::Mat plot = cv::Mat::zeros(plot_size, image_type);
00349 
00350     T min_value_ = get_min_value(begin, end, min_value);
00351     T max_value_ = get_max_value(begin, end, max_value);
00352 
00353     int old_x = 0;
00354     int old_y = 0;
00355     size_t const num_values = std::distance(begin, end);
00356     Iterator it = begin;
00357     for(size_t i=0; i<num_values; ++i, ++it)
00358     {
00359       int x = float(i)/float(num_values) * plot_size.width;
00360       int y = (float(*it - min_value_) / float(max_value_ - min_value_)) * plot_size.height;
00361       y = std::max(0, std::min(plot_size.height-1, y));
00362 
00363       cv::line(plot, cv::Point(old_x, plot_size.height - old_y - 1), cv::Point(x, plot_size.height - y - 1), line_color, line_width);
00364       old_x = x;
00365       old_y = y;
00366     }
00367 
00368     if(write_limits)
00369     {
00370       auto to_string = [](T value)
00371       {
00372         std::ostringstream out;
00373         out << std::setprecision(2) << std::fixed << value;
00374         return out.str();
00375       };
00376 
00377       int baseline;
00378       auto const font = CV_FONT_HERSHEY_PLAIN;
00379       float const font_scale = 1.0;
00380       cv::Size max_loc = cv::getTextSize(to_string(max_value_), font, font_scale, 1, &baseline);
00381       cv::putText(plot, to_string(max_value_), cv::Point(0, max_loc.height+5), font, font_scale, cv::Scalar::all(255));
00382       cv::putText(plot, to_string(min_value_), cv::Point(0, plot.rows-5), font, font_scale, cv::Scalar::all(255));
00383     }
00384     return plot; 
00385   }
00386 
00392   // ######################################################################
00394 
00406   class cubehelix
00407   {
00408     public:
00409 
00411 
00417       struct create
00418       {
00419         create() :
00420           nlev_(256), start_(0.5), rot_(-1.5), gamma_(1.0), hue_(1.2), reverse_(false) 
00421         { }
00422 
00423         operator cubehelix()
00424         {
00425           return cubehelix(nlev_, start_, rot_, gamma_, hue_, reverse_);
00426         }
00427 
00429         create & nlev(size_t val)  { nlev_ = val; return *this; }
00430 
00432         create & start(float val)  { start_ = val; return *this; }
00433 
00435 
00437         create & rot(float val)    { rot_ = val; return *this; }
00438 
00440         create & gamma(float val)  { gamma_ = val; return *this; }
00441 
00443         create & hue(float val)    { hue_ = val; return *this; }
00444 
00446 
00447         create & reverse()         { reverse_ = !reverse_; return *this; }
00448 
00449         size_t nlev_;
00450         float start_, rot_, gamma_, hue_;
00451         bool reverse_;
00452       };
00453 
00454 
00456 
00472       cubehelix(size_t nlev=256, float start=0.5, float rot=-1.5, float gamma=1.0, float hue=1.2, bool reverse=false) :
00473         nlev(nlev)
00474     {
00475       // Set up the parameters
00476       std::vector<double> fract(nlev);
00477       std::iota(fract.begin(), fract.end(), 0);
00478       for(auto & f : fract) f /= (nlev-1.0);
00479 
00480       std::vector<double> angle(nlev);
00481       std::transform(fract.begin(), fract.end(), angle.begin(),
00482           [start, rot](double f) { return 2*M_PI * (start/3.0 + 1.0 + rot*f); });
00483 
00484       for(auto & f : fract) f = std::pow(f, gamma);
00485 
00486       std::vector<double> amp(nlev);
00487       std::transform(fract.begin(), fract.end(), amp.begin(),
00488           [hue](double f) { return hue * f * (1.0-f)/2.0; });
00489 
00490       // compute the RGB vectors according to main equations
00491       red_.resize(nlev);
00492       grn_.resize(nlev);
00493       blu_.resize(nlev);
00494       for(size_t i=0; i<nlev; ++i)
00495       {
00496         double const s = std::sin(angle[i]);
00497         double const c = std::cos(angle[i]);
00498         double const f = fract[i];
00499         double const a = amp[i];
00500         red_[i] = std::max(0.0, std::min( 255.0, std::round((f+a*(-0.14861*c + 1.78277*s))*255.0)));
00501         grn_[i] = std::max(0.0, std::min( 255.0, std::round((f+a*(-0.29227*c - 0.90649*s))*255.0)));
00502         blu_[i] = std::max(0.0, std::min( 255.0, std::round((f+a*(1.97294*c))*255.0)));
00503       }
00504 
00505 
00506       if(reverse)
00507       {
00508         std::reverse(red_.begin(), red_.end());
00509         std::reverse(grn_.begin(), grn_.end());
00510         std::reverse(blu_.begin(), blu_.end());
00511       }
00512     }
00513 
00515       cv::Mat operator()(cv::Mat const & input)
00516       {
00517         if(input.channels() != 1)
00518           throw std::runtime_error(
00519               "Too many channels (" + std::to_string(input.channels()) + ") in input image");
00520 
00521         return RCV_DISPATCH(input.type(), process, input);
00522       }
00523 
00524    private:
00525       template<class T>
00526       cv::Mat process(cv::Mat const & input)
00527       {
00528         double minv, maxv;
00529         cv::minMaxLoc(input, &minv, &maxv);
00530 
00531         if(minv == maxv) return cv::Mat::zeros(input.rows, input.cols, CV_8UC3);
00532         cv::Mat ret(input.size(), CV_8UC3);
00533 
00534         std::transform(input.begin<T>(), input.end<T>(), ret.begin<cv::Vec3b>(),
00535             [this, minv, maxv](T const v) 
00536             { 
00537               size_t const idx = (v - minv) / (maxv - minv) * (nlev - 1);
00538               return cv::Vec3b(red_[idx], blu_[idx], grn_[idx]);
00539             });
00540 
00541         return ret;
00542       }
00543 
00544       size_t const nlev;
00545       std::vector<uint8_t> red_, blu_, grn_;
00546   };
00549 }
00550 
00559 #endif // RCV_HPP
00560 
 All Classes Functions