如何在不改变其他通道的情况下有效地将cv :: Mat的给定通道设置为给定值?

her*_*tao 15 c++ opencv matrix alpha-transparency color-channel

如何在cv::Mat不改变其他通道的情况下有效地将a的给定通道设置为给定值?例如,我想将其第四个通道(alpha通道)值设置为120(即半透明),如:

cv::Mat mat; // with type CV_BGRA
...
mat.getChannel(3) = Scalar(120); // <- this is what I want to do
Run Code Online (Sandbox Code Playgroud)

PS:我当前的解决方案首先分成mat多个通道并设置alpha通道,然后将它们合并.

PS2:我知道如果我也想通过以下方式更改其他频道,我可以快速完成此操作:

mat.setTo(Scalar(54, 154, 65, 120)); 
Run Code Online (Sandbox Code Playgroud)

使用通用解决方案更新:

这两种方法都可以将给定通道的所有mat值设置为给定值.它们将适用于所有矩阵,无论它们是否连续.

方法-1 - 更有效

- >基于@Antonio的答案,并由@MichaelBurdinov进一步改进

// set all mat values at given channel to given value
void setChannel(Mat &mat, unsigned int channel, unsigned char value)
{
    // make sure have enough channels
    if (mat.channels() < channel + 1)
        return;

    const int cols = mat.cols;
    const int step = mat.channels();
    const int rows = mat.rows;
    for (int y = 0; y < rows; y++) {
        // get pointer to the first byte to be changed in this row
        unsigned char *p_row = mat.ptr(y) + channel; 
        unsigned char *row_end = p_row + cols*step;
        for (; p_row != row_end; p_row += step)
            *p_row = value;
    }
}
Run Code Online (Sandbox Code Playgroud)

方法-2 - 更优雅

- >基于@ MichaelBurdinov的回答

// set all mat values at given channel to given value
void setChannel(Mat &mat, unsigned int channel, unsigned char value)
{
    // make sure have enough channels
    if (mat.channels() < channel+1)
        return;

    // check mat is continuous or not
    if (mat.isContinuous())
        mat.reshape(1, mat.rows*mat.cols).col(channel).setTo(Scalar(value));
    else{
        for (int i = 0; i < mat.rows; i++)
            mat.row(i).reshape(1, mat.cols).col(channel).setTo(Scalar(value));
    }
}
Run Code Online (Sandbox Code Playgroud)

PS:值得注意的是,根据文档,创建的矩阵Mat::create()总是连续的.但是,如果使用Mat::col(),Mat::diag()等等提取矩阵的一部分,或者为外部分配的数据构造矩阵头,则此类矩阵可能不再具有此属性.

Mic*_*nov 12

如果您的图像在内存中是连续的,您可以使用以下技巧:

mat.reshape(1,mat.rows*mat.cols).col(3).setTo(Scalar(120));
Run Code Online (Sandbox Code Playgroud)

如果不连续:

for(int i=0; i<mat.rows; i++)
    mat.row(i).reshape(1,mat.cols).col(3).setTo(Scalar(120));
Run Code Online (Sandbox Code Playgroud)

编辑(感谢Antonio的评论):

请注意,此代码可能是最短的,并且它没有分配新的内存,但它根本没有效率.它可能比分割/合并方法更慢.当OpenCV应该对连续1个像素的非连续矩阵执行操作时,效率非常低.如果时间表现很重要,你应该使用@Antonio提出的解决方案.

只是对他的解决方案的一个小改进:

const int cols = img.cols;
const int step = img.channels();
const int rows = img.rows;
for (int y = 0; y < rows; y++) {
    unsigned char* p_row = img.ptr(y) + SELECTED_CHANNEL_NUMBER; //gets pointer to the first byte to be changed in this row, SELECTED_CHANNEL_NUMBER is 3 for alpha
    unsigned char* row_end = p_row + cols*step;
    for(; p_row != row_end; p_row += step)
         *p_row = value;
    }
}
Run Code Online (Sandbox Code Playgroud)

这样可以节省x的递增操作和寄存器中的少一个值.在资源有限的系统上,它可以提供约5%的加速.否则时间表现将是相同的.

  • +1我真的很想知道这个实现的效率是多少,更一般地说,为什么从事计算机视觉工作的人如此害怕通过矩阵编写函数并改变字节值. (2认同)

Ant*_*nio 7

Mat img;
[...]
const int cols = img.cols;
const int step = img.channels();
const int rows = img.rows;
for (int y = 0; y < rows; y++) {
    unsigned char* p_row = img.ptr(y) + SELECTED_CHANNEL_NUMBER; //gets pointer to the first byte to be changed in this row, SELECTED_CHANNEL_NUMBER is 3 for alpha
    for (int x = 0; x < cols; x++) {
         *p_row = value;
         p_row += step; //Goes to the next byte to be changed
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:根据opencv术语的使用,这适用于连续矩阵和非连续矩阵:http://docs.opencv.org/modules/core/doc/basic_structures.html#bool%20Mat:: isContinuous%28% 29%20const