c++opencv

Initialize an OpenCV Mat with pixel data in RGBA channel order


I am writing an effect plug-in for Adobe After Effects in C++, and I need to create a cv::Mat using pixel data that's already in memory. However, when I initialize it using:

cv::Mat in_frame_mat(height, width, CV_8UC4, input->data);

(input->data is a pointer to the first byte), the output is all blue.
I think this is because AE stores pixel data RGBA or ARGB while OpenCV assumes BGRA or ABGR (not sure which).

I know that I could iterate through every pixel and create a new space in memory to store a BGRA representation of the image, then initialize a cv::Mat with that, but this is really performance constrained and I don't want to add unnecessary compute time.

Is there a way to create a cv::Mat using existing pixel data that is stored RGBA?


Solution

  • OpenCV assumes the channel order is BGR (or BGRA).

    You can use cv::cvtColor to change your input to BGRA. The 3rd parameter code should be one of the values from cv::ColorConversionCodes, e.g.: cv::COLOR_RGBA2BGRA (seems to be what you need).


    Update:

    Following the OP's comment below that his input is actually ARGB (not RGBA):
    ARGB to BGRA is not supported by cv::cvtColor.
    But you can use cv:mixChannels to do any arbitrary reordering of the channels (the fromTo parameter specifies the reordering).

    See below how to use it to convert ARGB to BGRA:

    #include <vector>
    cv::Mat argb; // initialized from your input data
    cv::Mat bgra(argb.size(), argb.type());
    std::vector<int> fromTo{ 0,3, 1,2, 2,1, 3,0 };  // ARGB->BGRA: 0->3, 1->2, 2->1, 3->0
    cv::mixChannels(argb, bgra, fromTo);
    

    If your OpenCV version does not support this flavor of cv::mixChannels (with std::vector etc.), you can try:

    cv::Mat argb; // initialized from your input data
    cv::Mat bgra(argb.size(), argb.type());
    int fromTo[] = { 0,3, 1,2, 2,1, 3,0 };    // ARGB->BGRA: 0->3, 1->2, 2->1, 3->0
    cv::mixChannels(&argb, 1, &bgra, 1, fromTo, 4);
    

    Update 2:

    In order to convert ARGB to BGR (i.e. without the alpha channel), you can use cv::mixChannels in the following way:

    #include <vector>
    cv::Mat argb; // initialized from your input data
    cv::Mat bgr(argb.size(), CV_8UC3); // NOTE: the type is CV_8UC3, not argb.type()
    std::vector<int> fromTo{ 1,2, 2,1, 3,0 };  // ARGB->BGR: 1->2, 2->1, 3->0
    cv::mixChannels(argb, bgr, fromTo);