How to set given channel of a cv::Mat
to a given value efficiently without changing other channels? For example, I want to set its fourth channel (alpha channel
If your image is continuous in memory you can use following trick:
mat.reshape(1,mat.rows*mat.cols).col(3).setTo(Scalar(120));
If it is not continuous:
for(int i=0; i<mat.rows; i++)
mat.row(i).reshape(1,mat.cols).col(3).setTo(Scalar(120));
Edit (thanks to Antonio for the comment):
Note that this code may be the shortest and it is not allocating new memory but it is not efficient at all. It may be even slower than split/merge approach. OpenCV is really inefficient when it should perform operations on non-continuous matrices with 1 pixel in a row. If time performance is important you should use the solution proposed by @Antonio.
Just a minor improvement to his solution:
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;
}
}
This saves increment operation for x and one less value in register. On system with limited resources it may give ~5% speedup. Otherwise time performance will be the same.
What about direct Mat::data access (I'm quite sure setTo() or oother opencv Mat api use similar solution):
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;
}
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
}
}
Note: This works both for continuous and uncontinuous matrices, according to the use of the term for opencv: http://docs.opencv.org/modules/core/doc/basic_structures.html#bool%20Mat::isContinuous%28%29%20const
Simple algorithm:
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;
}