rcv
Rand's OpenCV Utilities
|
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