Я хочу создать свою собственную функцию SetChannel
, которая будет устанавливать определенный канал изображения. Например, у меня есть изображение input
типа CV_16UC3
(изображение BGR типа ushort
), и я хочу изменить зеленый канал (=1 из-за нулевого индекса) на ushort
значение 32768
. Для этого я призываю SetChannel(input,1,32768)
.
template<typename T>
void SetChannel(Mat mat, uint channel, T value)
{
const uint channels = mat.channels();
if (channel + 1 > channels)
return;
T * data = (T*)mat.data;
// MBPR : number of Memory Block Per Row
// Mat.step : number of byte per row
// Mat.elemSize1() : number of byte per channel
const unsigned int MBPR = mat.step / mat.elemSize1();
// N : total number of memory blocks
const unsigned int N = mat.rows * MBPR;
for (uint i = channel; i < N; i += channels)
data[i] = value;
}
Я предпочитаю работать в одном цикле, а не вложенном цикле, поэтому я определяю количество итераций N
, как указано выше.
Приведенный выше код работает так, как ожидалось но некоторые другие люди сказали, что часть
T * data = (T*)mat.data;
является запахом кода и считается плохо разработанной программой.
Теперь я хочу переписать новый с другим подходом следующим образом.
Это не работает должным образом, потому что я не знаю, как присвоить T value
data[i]
типа uchar
.
template<typename T>
void SetChannel(Mat mat, uint channel, T value)
{
const uint channels = mat.channels();
if (channel + 1 > channels)
return;
uchar * data = mat.data;
const unsigned int N = mat.rows * mat.step;// byte per image
const unsigned int bpc = mat.elemSize1();// byte per channel
const unsigned int bpp = mat.elemSize(); // byte per pixel
for (uint i = channel * bpc; i < N; i += bpp)
//data[i] = value;
}
Как без потерь присвоить value
типа T
data[i]
типа uchar
?
Для тех, кто не знает, что такое Mat
, может быть полезно следующее.
Mat
OpenCV предоставляет множество типов изображений. Например,
CV_8UC1
представляет тип изображения в градациях серого, в котором каждый пиксель имеет один канал типа uchar
.CV_8UC3
представляет тип изображения BGR (не RGB), в котором каждый пиксель имеет три канала, каждый типа uchar
.CV_16UC3
представляет тип изображения BGR (не RGB), в котором каждый пиксель имеет три канала, каждый типа ushort
.
и т.п.
Mat
— это класс для инкапсуляции изображения. Он имеет несколько атрибутов и функций. Позвольте мне перечислить некоторые из них, которые я буду использовать в этом вопросе, чтобы вы могли лучше понять мой сценарий.
Mat.data
: указатель типа uchar
, указывающий на блок пикселей изображения.Mat.rows
: количество рядовMat.channels()
: количество каналов на пиксельMat.elemSize1()
(заканчивается на 1): количество байтов на каналMat.elemSize()
: количество байтов на пиксель.
Mat.elemSize() = Mat.channels() * Mat.elemSize1()
.Mat.step
: количество байтов в строкеЗдесь Mat.step
можно рассматривать как произведение
- "эффективное" количество пикселей в строке (назовем его EPPR),
- количество каналов на пиксель или Mat.channels()
, и
- количество байт на канал или Mat.elemSize1()
.
Математически,
Mat.step = EPPR * Mat.elemSize()
Mat.step = EPPR * Mat.channels() * Mat.elemSize1()
Позвольте мне определить EPPR * Mat.channels()
как блоки памяти на строку (MBPR
). Если вы знаете правильный термин для MBPR
, дайте мне знать.
В результате MBPR = Mat.step / Mat.elemSize1()
.
Я получил это от кого-то в автономном режиме. Надеюсь, это полезно и для других.
template<typename T>
void SetChannel(Mat mat, uint channel, T value)
{
const uint channels = mat.channels();
if (channel + 1 > channels)
return;
uchar * data = mat.data;
const unsigned int N = mat.rows * mat.step;// byte per image
const unsigned int bpc = mat.elemSize1();// byte per channel
const unsigned int bpp = mat.elemSize(); // byte per pixel
const unsigned int bpu = CHAR_BIT * sizeof(uchar);// bits per uchar
for (uint i = channel * bpc; i < N; i += bpp)
for (uint j = 0; j < bpc; j++)
data[i + j] = value >> bpu * j;
}
К сожалению, из-за вложенных циклов этот новый метод работает медленнее предыдущего примерно в 2 раза.
У вас должен быть тест в шаблоне, который проверяет T, чтобы убедиться, что он совместим с uchar, а затем использует
static_cast<T*>
вместо C-приведения.