2013-06-11 35 views
7

Tôi có nên biết Mat loại yếu tố để sử dụng at() chính xác không? Ví dụ, nếu tôi cóLàm cách nào để truy cập/sửa đổi phần tử ma trận trong OpenCV? Tại sao tại() là templatized?

Mat rose = Mat(1,180, CV_64F, 0); 

sau đó tôi có thể gọi

rose.at<short>(i,j)++; 

Nếu không sau đó mẫu đối số tôi nên sử dụng?

Tại sao Mat::at được templatized trong khi Mat chính nó thì không?

CẬP NHẬT

này câu hỏi chứa mẫu mã với một lỗi khác, mà bây giờ ở đây: How to fill Matrix with zeros in OpenCV?

+0

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

+0

Kiểm tra cũng [này] (http://stackoverflow.com/questions/15965957/open-cv-generic-mat-function-headers/). – William

+0

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'. –

Trả lời

6

Như đã đúng chỉ ra bởi William, bạn nên cung cấp chỉ đúng loại như một mẫu đối số cho at. Tôi tin rằng bản thân cv::Mat không được tạo mẫu để đơn giản hóa. Tuy nhiên, phi hành đoàn OpenCV đang cố gắng hỗ trợ các tính năng của C++, bao gồm các mẫu. Giao diện trở nên hơi không đồng nhất theo cách này.

Bạn không thể suy ra loại trình biên dịch từ một loại biến trong thời gian chạy vì lý do rõ ràng. Tuy nhiên, bạn có thể suy ra nó ở thời gian biên dịch, nếu loại biến của bạn được biết đến vào thời điểm này, sử dụng một lớp học đặc điểm:

template<int I> 
struct CvType {}; 

template<> 
struct CvType<CV_64F> { typedef double type_t; }; 
template<> 
struct CvType<CV_32F> { typedef float type_t; }; 
template<> 
struct CvType<CV_8U> { typedef unsigned char type_t; }; 
// Other types go here 

void main() 
{ 
    const int type = CV_64F; 
    cv::Mat mat(10, 10, type); 
    mat.at<CvType<type>::type_t>(1, 1); 
} 

Trong trường hợp này bạn có thể thay đổi giá trị của type và sẽ không cần phải thay đổi loại thủ công cho tất cả các cuộc gọi at hoặc các phương thức khác.

+0

OK Tôi thấy ý tưởng của bạn. Nhưng việc sử dụng 'double' không làm việc cho tôi với' CV_64F', đây là tất cả những gì về nó. Tôi đã thử 'double' ngay từ đầu, bởi vì tôi rõ ràng đã đọc tài liệu về' CV_64F'. Xin vui lòng xem bản cập nhật của tôi. –

+1

Đối số cuối cùng cho 'cv :: Mat' constructor trong trường hợp của bạn là một con trỏ đến dữ liệu người dùng được phân bổ, _not_ mặc định fill-in của hình ảnh. Vì vậy, trong trường hợp của bạn, bạn đang cố gắng truy cập bộ nhớ với địa chỉ bằng không. Loại bỏ nó và bạn ổn. Hoặc thay thế nó bằng 'cv :: Scalar (0)'. – Mikhail

4

Vâng bây giờ bạn sửa bài khác. Sửa chữa:

Mat m1 = Mat(1,1, CV_64F, cvScalar(0.)); 
m1.at<double>(0,0) = 0; 

hoặc thử khác nhau:

Mat m1 = cv::Mat::zeros(1,1, CV_64F); 
m1.at<double>(0,0) = 0; 
+0

Tại sao tôi nên thử hàm tạo khác nhau? Có chuyện gì với tôi? –

+0

Bạn cần sử dụng cấu trúc cv làm giá trị. Sử dụng 'cvScalar (0)' để thay thế. – William

+0

@SuzanCioc Tôi biết tôi đã khắc phục lỗi của bạn nhưng tôi đồng ý cung cấp tín dụng cho đoạn trích Mikhail tuyệt vời được đề xuất. Tuy nhiên, lát của câu hỏi của bạn là sai, dựa trên mã mẫu của bạn. – William

2

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à tCV_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>; 
Các vấn đề liên quan