簡體   English   中英

如何在不更改其他通道的情況下有效地將 cv::Mat 的給定通道設置為給定值?

[英]How to set given channel of a cv::Mat to a given value efficiently without changing other channels?

如何在不更改其他通道的情況下有效地將cv::Mat的給定通道設置為給定值? 例如,我想將它的第四個通道(alpha 通道)值設置為120 (即半透明),類似於:

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

PS:我目前的解決方案是先將mat拆分為多個通道並設置alpha通道,然后將它們合並回來。

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

mat.setTo(Scalar(54, 154, 65, 120)); 

使用通用解決方案進行更新:

這兩種方法都適用於將給定通道的所有墊子值設置為給定值。 它們適用於所有矩陣,無論它們是否連續。

方法 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;
    }
}

方法 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));
    }
}

PS:值得注意的是,根據文檔,使用Mat::create()矩陣始終是連續的。 但是,如果您使用Mat::col()Mat::diag()提取矩陣的一部分,或者為外部分配的數據構造矩陣頭,則此類矩陣可能不再具有此屬性。

如果您的圖像在內存中是連續的,您可以使用以下技巧:

mat.reshape(1,mat.rows*mat.cols).col(3).setTo(Scalar(120));

如果不連續:

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

編輯(感謝安東尼奧的評論):

請注意,此代碼可能是最短的,它沒有分配新內存,但根本沒有效率。 它可能比拆分/合並方法還要慢。 當 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;
    }
}

這節省了 x 的遞增操作和寄存器中少一個值。 在資源有限的系統上,它可能會提供約 5% 的加速。 否則時間性能將是相同的。

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
    }
}

注意:根據 opencv 術語的使用,這適用於連續和不連續矩陣: http ://docs.opencv.org/modules/core/doc/basic_structures.html#bool%20Mat:: isContinuous%28% 29%20const

直接 Mat::data 訪問怎么樣(我很確定 setTo() 或其他 opencv Mat api 使用類似的解決方案):

template<int N>
void SetChannel(Mat &img, unsigned char newVal) {   
    for(int x=0;x<img.cols;x++) {
        for(int y=0;y<img.rows;y++) {
            *(img.data + (y * img.cols + x) * img.channels() + N) = newVal;
        }
    }
}


int main() {
    Mat img = Mat::zeros(1000, 1000, CV_8UC4);
    SetChannel<3>(img, 120);
    imwrite("out.jpg", img);

    return 0;
}

簡單算法:

void SetChannel(Mat mat, uint channel, uchar value)
{
    const uint channels = mat.channels();
    if (channel > channels - 1)
        return;

    uchar * data = mat.data;
    uint N = mat.rows * mat.step / mat.elemSize1();

    for (uint i = channel; i < N; i += channels)
        data[i] = value;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM