Các Mat
lớp không phải là một mẫu lớp, cho phép thay đổi "loại" của nó khi chạy. Thay đổi loại là hữu ích, ví dụ: khi đọc từ một tập tin. Không phải là một mẫu tiện lợi, khi sử dụng tham số hàm Mat
, vì hàm này không bắt buộc phải là một hàm mẫu.
Tuy nhiên, để truy cập vào các phần tử đơn lẻ (với pointers, at hoặc iterators), bạn cần loại dữ liệu. Tôi đoán điều này được thực hiện vì lý do hiệu suất. Điều này mâu thuẫn với hệ thống kiểu thời gian chạy và làm cho nó khó hơn để viết mã chung, khi bạn không biết loại tại thời gian biên dịch. Tuy nhiên, bạn có thể làm điều đó với một workaround.
Phương pháp đơn giản nhất là chỉ để sử dụng một if-else-cascade:
Mat img = imread("test.png");
if (img.channels() == 1) {
if (img.depth() == CV_8U) {
cout << (int)(img.at<uint8_t>(0,0)) << endl;
}
else if (img.depth() == CV_8S) {
/* ... */
}
/* ... */
}
/* ... */
else if (img.channels() == 3) {
if (img.depth() == CV_8U) {
auto p = img.at<array<uint8_t,3>>(0,0);
cout << (int)(p[0]) << ";" << (int)(p[1]) << ";" << (int)(p[2]) << endl;
}
/* ... */
}
/* ... */
Nhưng bạn có thể tưởng tượng rằng điều này trở nên cồng kềnh, nếu bạn viết nó ra cho tất cả các loại và các kênh truyền hình. Tuy nhiên, bạn phải giới hạn số lượng kênh vì không có giới hạn cứng của OpenCV. Chúng tôi chọn 4 mục sau.
Tôi đã viết một tiêu đề metaprogram helper mẫu, mà không được công việc. Bạn có thể cung cấp một functor với một templated operator()
.Sau đó, gọi metaprogram mẫu, sẽ gọi hàm functor với một kiểu thời gian biên dịch. Xem ví dụ này cho một functor rằng in pixel đầu tiên và trả về cho dù đó không phải là tất cả zero:
struct PrintPixel {
Mat img;
// this template function will be called from the metaprogram
template<int cv_type> // compile time value e.g. CV_8UC3
bool operator()() {
using elem_t = typename CvTypeTraits<cv_type>::base_type;
using array_t = typename CvTypeTraits<cv_type>::array_type;
// you could also do static_asserts here
array_t pixel = img.at<array_t>(0,0);
for (elem_t val : pixel)
cout << (double)(val) << ", ";
cout << endl;
return any_of(pixel.begin(), pixel.end(), [](elem_t v){return v != 0;});
}
};
Lưu ý, kiểu trả về của operator()
có thể tùy ý, nhưng có thể không phụ thuộc vào loại hình ảnh cv_type
không may. Đây là bởi vì nó cũng được sử dụng như là kiểu trả về của hàm chứa if-else thác (các run
chức năng, xem dưới đây).
Đây là mã gọi điện thoại, có thể kiểm tra xem có "tất cả" các kênh truyền hình (1-4) và các loại hoặc cho một bộ quy định:
Mat img = imread("test.png");
int t = img.type();
// call functor, check for 1-4 channels and all 7 base types
bool not_zero = CallFunctor::run(PrintPixel{img}, t);
// call functor, check only for 1 or 3 channels and 8 bit unsigned int
CallFunctorRestrictChannelsTo<1,3>::AndBaseTypesTo<CV_8U>::run(PrintPixel{img}, t);
Cuộc gọi thứ hai sẽ ném một ngoại lệ, nếu không phải là t
CV_8UC1
hoặc CV_8UC3
. Nếu bạn thường sử dụng cùng một hạn chế, bạn có thể viết tắt nó bằng một khai báo sử dụng (xem ở dưới cùng của tệp tiêu đề bên dưới).
Vì vậy, đây là một giải pháp dễ dàng sử dụng, cho phép bạn sử dụng một giá trị thời gian biên dịch "made" từ một giá trị thời gian chạy. Nhưng hãy nhớ, trong nền một if-else-cascade đang làm tất cả các kiểm tra (theo thứ tự các kênh và các loại đã được chỉ định). Điều này ngụ ý rằng đối với mỗi kết hợp của kênh và kiểu được kiểm tra, một lớp functor cụ thể được tạo ra. Nếu nó lớn, điều đó có thể xấu. Vì vậy, nó chỉ nên bao gồm các phần phụ thuộc vào loại. Nó cũng có thể là một nhà máy functor mà instanciates một lớp templated với một cơ sở không mẫu với các chức năng ảo để giảm kích thước mã.
Nó sau các tập tin tiêu đề, trong đó có lớp CvTypeTraits
và các chức năng template metaprogram. Ở phía dưới, bạn có thể thấy rằng loại CallFunctor
thực sự chỉ là chữ viết tắt cho "hạn chế" của các loại và kênh. Bạn cũng có thể tuyên bố một cái gì đó như thế với các hạn chế khác.
#pragma once
#include <cstdint>
#include <type_traits>
#include <array>
#include <opencv2/core/types_c.h>
template<int> struct BaseType { };
template<> struct BaseType<CV_8S> { using base_type = int8_t; };
template<> struct BaseType<CV_8U> { using base_type = uint8_t; };
template<> struct BaseType<CV_16S> { using base_type = int16_t; };
template<> struct BaseType<CV_16U> { using base_type = uint16_t; };
template<> struct BaseType<CV_32S> { using base_type = int32_t; };
template<> struct BaseType<CV_32F> { using base_type = float; };
template<> struct BaseType<CV_64F> { using base_type = double; };
template<int t>
struct CvTypeTraits {
constexpr static int channels = t/CV_DEPTH_MAX + 1;
using base_type = typename BaseType<t % CV_DEPTH_MAX>::base_type;
using array_type = std::array<base_type, channels>;
};
template<int currentChannel, int... otherChannels>
struct find_chan_impl {
template<typename ret_type, int... types>
struct find_type_impl {
template<class Functor>
static inline ret_type run(Functor&& f, int const& c, int const& t) {
if (c == currentChannel)
return find_chan_impl<currentChannel>::template find_type_impl<ret_type, types...>::run(std::forward<Functor>(f), c, t);
else
return find_chan_impl<otherChannels...>::template find_type_impl<ret_type, types...>::run(std::forward<Functor>(f), c, t);
}
};
};
template<>
struct find_chan_impl<0> {
template<typename ret_type, int... types>
struct find_type_impl {
template<class Functor>
[[noreturn]] static inline ret_type run(Functor&& f, int const& c, int const& t) {
throw std::runtime_error("The image has " + std::to_string(c) + " channels, but you did not try to call the functor with this number of channels.");
}
};
};
template<int channel>
struct find_chan_impl<channel> {
template<typename ret_type, int currentType, int... otherTypes>
struct find_type_impl {
static_assert(currentType < CV_DEPTH_MAX, "You can only restrict to base types, without channel specification");
template<class Functor>
static inline ret_type run(Functor&& f, int const& c, int const& t) {
if (t == currentType)
return find_type_impl<ret_type, currentType>::run(std::forward<Functor>(f), c, t);
else
return find_type_impl<ret_type, otherTypes...>::run(std::forward<Functor>(f), c, t);
}
};
template<typename ret_type, int type>
struct find_type_impl<ret_type, type> {
template<class Functor>
static inline ret_type run(Functor&& f, int const& c, int const& t) {
return f.template operator()<CV_MAKETYPE(type,channel)>();
}
};
template<typename ret_type>
struct find_type_impl<ret_type, -1> {
template<class Functor>
[[noreturn]] static inline ret_type run(Functor&& f, int const& c, int const& t) {
throw std::runtime_error("The image is of base type " + std::to_string(t) + ", but you did not try to call the functor with this base type.");
}
};
};
template<int... channels>
struct CallFunctorRestrictChannelsTo {
template<int firstType, int... types>
struct AndBaseTypesTo {
template<class Functor>
static inline auto run(Functor&& f, int t) -> decltype(f.template operator()<firstType>()) {
using functor_ret_type = decltype(f.template operator()<firstType>());
std::div_t d = std::div(t, CV_DEPTH_MAX);
int c = d.quot + 1;
int const& base_t = d.rem;
return find_chan_impl<channels..., 0>::template find_type_impl<functor_ret_type, firstType, types..., -1>::run(std::forward<Functor>(f), c, base_t);
}
};
template<class Functor>
static inline auto run(Functor&& f, int t) -> decltype(f.template operator()<CV_8S>()) {
return AndBaseTypesTo<CV_8S, CV_8U, CV_16S, CV_16U, CV_32S, CV_32F, CV_64F>::run(std::forward<Functor>(f), t);
}
};
template<int... types>
using CallFunctorRestrictBaseTypesTo = CallFunctorRestrictChannelsTo<1,2,3,4>::template AndBaseTypesTo<types...>;
using CallFunctor = CallFunctorRestrictChannelsTo<1,2,3,4>::template AndBaseTypesTo<CV_8S, CV_8U, CV_16S, CV_16U, CV_32S, CV_32F, CV_64F>;
Về cơ bản, có. Bạn có trách nhiệm hoặc biết hoặc khám phá nó với 'Mat.type()'. – William
Kiểm tra cũng [này] (http://stackoverflow.com/questions/15965957/open-cv-generic-mat-function-headers/). – William
Vì vậy, trong ví dụ trên tôi biết loại hoặc có thể khám phá ra nó. Tiếp theo là gì? 'CV_64F' là một giá trị thời gian chạy, tôi không thể sử dụng nó trong tham số mẫu' at'. –