OpenCV将3个通道连接到32位值

OpenCV concatenate 3 channels to 32-bit value?

本文关键字:32位值 连接 通道 3个 OpenCV      更新时间:2023-10-16

我正在寻找在openCV中将3通道RGB帧转换为1通道图像的最快方法。但是我需要将像素的所有三种颜色(R, G, B)连接成一个32位值。

每个像素应该包含一个例子:

pixel[0:31]= 01001001 11110000 11111111 00000000

前8位是来自帧的红色(在帧中的相同位置),第二个8位是绿色,第三个8位是蓝色,最后8位不重要。

我试过了:

for (y = 100; y < 500; y++){
     for (x = 100; x < 500; x++) {
        int pixel =((edges.at<Vec3b>(y, x)[0])<<16)|
                    ((edges.at<Vec3b>(y, x)[1])<<8)|
                    (edges.at<Vec3b>(y, x)[2]);
}}

但是这是慢的,因为我需要遍历帧中的每个像素。

谢谢

尝试了一些变体:

  • 方法0a:如问题中所示,@Sveva
  • 方法0b:与问题中一样,使用颠倒的for循环,@Miki
  • 方法1:只使用索引查询一次mat的值,@Miki
  • 方法2:使用指针从mat中查询值,@Miki
  • 方法3:转换为BGRA和memcpy, @RyanP注意:仅当mat isContinuos()时有效。

结果(时间单位:毫秒)

isContinuos? 1
Method 0a: 113.704              0x1020300
Method 0b: 20.0975              0x1020300
Method 1: 20.1939               0x1020300
Method 2: 15.7434               0x1020300
Method 3: 22.5592               0xff030201

考虑

反转for循环有很大的加速,因为OpenCV Mat是行主要排序的。最快的方法是使用指针的方法2。方法1稍微慢一些,但可能更具可读性。方法3相当快,但是单个memcpy仅在矩阵isContinuos()时才有效。如果不是这种情况,您需要对每一行进行循环,并对每一行执行memcpy,这将会(只是一点点)慢。

注意

OpenCV存储BGR值(不是RGB)。方法0a、0b、1、2输出值为:B G R 0。你只需要交换索引0和2来得到R G B 0。对于方法3,您需要使用cvtColor和参数COLOR_BGR2RGBA

#include <opencv2opencv.hpp>
#include <vector>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
    // Test Image
    Mat3b img(2000, 3000, Vec3b(1, 2, 3));
    cout << "isContinuos? " << img.isContinuous() << endl;
    // Method 0a: method from question. Credits to @Sveto
    double tic0a = double(getTickCount());
    vector<int> v0a(img.rows * img.cols, 0);
    for (int c = 0; c < img.cols; ++c)
    {
        for (int r = 0; r < img.rows; ++r)
        {
            v0a[r*img.cols + c] = ((img.at<Vec3b>(r, c)[0]) << 24) |
                ((img.at<Vec3b>(r, c)[1]) << 16) |
                (img.at<Vec3b>(r, c)[2]) << 8;
        }
    }
    double toc0a = (double(getTickCount()) - tic0a) * 1000. / getTickFrequency();
    cout << "Method 0a: " << toc0a << "tt";;
    cout << "0x" << hex << v0a[0] << endl;
    // Method 0b: method from question, loops inverted
    double tic0b = double(getTickCount());
    vector<int> v0b(img.rows * img.cols, 0);
    for (int r = 0; r < img.rows; ++r)
    {
        for (int c = 0; c < img.cols; ++c)
        {
            v0b[r*img.cols + c] = ((img.at<Vec3b>(r, c)[0]) << 24) |
                ((img.at<Vec3b>(r, c)[1]) << 16) |
                (img.at<Vec3b>(r, c)[2]) << 8;
        }
    }
    double toc0b = (double(getTickCount()) - tic0b) * 1000. / getTickFrequency();
    cout << "Method 0b: " << toc0b << "tt";
    cout << "0x" << hex << v0b[0] << endl;

    // Method 1: custom loop with indices
    double tic1 = double(getTickCount());
    vector<int> v1(img.rows * img.cols, 0);
    for (int r = 0; r < img.rows; ++r)
    {
        for (int c = 0; c < img.cols; ++c)
        {
            const Vec3b& b = img(r, c);
            v1[r*img.cols + c] = (b[0] << 24) | (b[1] << 16) | (b[2] << 8);
        }
    }
    double toc1 = (double(getTickCount()) - tic1) * 1000. / getTickFrequency();
    cout << "Method 1: " << toc1 << "tt";
    cout << "0x" << hex << v1[0] << endl;
    // Method 2: custom loop with pointers
    double tic2 = double(getTickCount());
    vector<int> v2(img.rows * img.cols, 0);
    for (int r = 0; r < img.rows; ++r)
    {
        uchar* p = img.ptr<uchar>(r);
        for (int c = 0; c < img.cols; ++c)
        {
            int val = ((*p) << 24); ++p;
            val |= ((*p) << 16); ++p;
            val |= ((*p) << 8); ++p;
            v2[r*img.cols + c] = val;
        }
    }
    double toc2 = (double(getTickCount()) - tic2) * 1000. / getTickFrequency();
    cout << "Method 2: " << toc2 << "tt";
    cout << "0x" << hex << v2[0] << endl;

    // Method 3: using BGRA conversion. Credits @RyanP
    // NOTE: works only if img.isContinuos()
    double tic3 = double(getTickCount());
    Mat4b rgba3;
    cvtColor(img, rgba3, COLOR_BGR2BGRA);
    vector<int> v3(img.rows * img.cols, 0);
    memcpy(v3.data(), rgba3.data, img.rows * img.cols * sizeof(int));
    double toc3 = (double(getTickCount()) - tic3) * 1000. / getTickFrequency();
    cout << "Method 3: " << toc3 << "tt";
    cout << "0x" << hex << v3[0] << endl;
    int dummy;
    cin >> dummy;
    return 0;
}

使用拆分和合并通道功能

看起来很复杂,但比每次处理一个像素要容易得多。

参见stackoverflow.com/questions/14582082/merging-channels-in-opencv查看示例